aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/devfreq
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/devfreq')
-rw-r--r--drivers/devfreq/Kconfig2
-rw-r--r--drivers/devfreq/Makefile3
-rw-r--r--drivers/devfreq/devfreq-event.c494
-rw-r--r--drivers/devfreq/event/Kconfig25
-rw-r--r--drivers/devfreq/event/Makefile2
-rw-r--r--drivers/devfreq/event/exynos-ppmu.c374
-rw-r--r--drivers/devfreq/event/exynos-ppmu.h93
7 files changed, 993 insertions, 0 deletions
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 4aab799712bb..51dccb3620ea 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -97,4 +97,6 @@ config ARM_TEGRA_DEVFREQ
97 It reads ACTMON counters of memory controllers and adjusts the 97 It reads ACTMON counters of memory controllers and adjusts the
98 operating frequencies and voltages with OPP support. 98 operating frequencies and voltages with OPP support.
99 99
100source "drivers/devfreq/event/Kconfig"
101
100endif # PM_DEVFREQ 102endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 0ea991f3a483..9256c35bcce9 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -8,3 +8,6 @@ obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
8obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ 8obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/
9obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ 9obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/
10obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o 10obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
11
12# DEVFREQ Event Drivers
13obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
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");
diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
new file mode 100644
index 000000000000..a11720affc31
--- /dev/null
+++ b/drivers/devfreq/event/Kconfig
@@ -0,0 +1,25 @@
1menuconfig PM_DEVFREQ_EVENT
2 bool "DEVFREQ-Event device Support"
3 help
4 The devfreq-event device provide the raw data and events which
5 indicate the current state of devfreq-event device. The provided
6 data from devfreq-event device is used to monitor the state of
7 device and determine the suitable size of resource to reduce the
8 wasted resource.
9
10 The devfreq-event device can support the various type of events
11 (e.g., raw data, utilization, latency, bandwidth). The events
12 may be used by devfreq governor and other subsystem.
13
14if PM_DEVFREQ_EVENT
15
16config DEVFREQ_EVENT_EXYNOS_PPMU
17 bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
18 depends on ARCH_EXYNOS
19 select PM_OPP
20 help
21 This add the devfreq-event driver for Exynos SoC. It provides PPMU
22 (Platform Performance Monitoring Unit) counters to estimate the
23 utilization of each module.
24
25endif # PM_DEVFREQ_EVENT
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
new file mode 100644
index 000000000000..be146ead79cf
--- /dev/null
+++ b/drivers/devfreq/event/Makefile
@@ -0,0 +1,2 @@
1# Exynos DEVFREQ Event Drivers
2obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c
new file mode 100644
index 000000000000..135be0aada9d
--- /dev/null
+++ b/drivers/devfreq/event/exynos-ppmu.c
@@ -0,0 +1,374 @@
1/*
2 * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support
3 *
4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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/exynos/exynos_ppmu.c
12 */
13
14#include <linux/clk.h>
15#include <linux/io.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/of_address.h>
20#include <linux/platform_device.h>
21#include <linux/suspend.h>
22#include <linux/devfreq-event.h>
23
24#include "exynos-ppmu.h"
25
26struct exynos_ppmu_data {
27 void __iomem *base;
28 struct clk *clk;
29};
30
31struct exynos_ppmu {
32 struct devfreq_event_dev **edev;
33 struct devfreq_event_desc *desc;
34 unsigned int num_events;
35
36 struct device *dev;
37 struct mutex lock;
38
39 struct exynos_ppmu_data ppmu;
40};
41
42#define PPMU_EVENT(name) \
43 { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \
44 { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \
45 { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \
46 { "ppmu-event3-"#name, PPMU_PMNCNT3 }
47
48struct __exynos_ppmu_events {
49 char *name;
50 int id;
51} ppmu_events[] = {
52 /* For Exynos3250, Exynos4 and Exynos5260 */
53 PPMU_EVENT(g3d),
54 PPMU_EVENT(fsys),
55
56 /* For Exynos4 SoCs and Exynos3250 */
57 PPMU_EVENT(dmc0),
58 PPMU_EVENT(dmc1),
59 PPMU_EVENT(cpu),
60 PPMU_EVENT(rightbus),
61 PPMU_EVENT(leftbus),
62 PPMU_EVENT(lcd0),
63 PPMU_EVENT(camif),
64
65 /* Only for Exynos3250 and Exynos5260 */
66 PPMU_EVENT(mfc),
67
68 /* Only for Exynos4 SoCs */
69 PPMU_EVENT(mfc-left),
70 PPMU_EVENT(mfc-right),
71
72 /* Only for Exynos5260 SoCs */
73 PPMU_EVENT(drex0-s0),
74 PPMU_EVENT(drex0-s1),
75 PPMU_EVENT(drex1-s0),
76 PPMU_EVENT(drex1-s1),
77 PPMU_EVENT(eagle),
78 PPMU_EVENT(kfc),
79 PPMU_EVENT(isp),
80 PPMU_EVENT(fimc),
81 PPMU_EVENT(gscl),
82 PPMU_EVENT(mscl),
83 PPMU_EVENT(fimd0x),
84 PPMU_EVENT(fimd1x),
85 { /* sentinel */ },
86};
87
88static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
89{
90 int i;
91
92 for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
93 if (!strcmp(edev->desc->name, ppmu_events[i].name))
94 return ppmu_events[i].id;
95
96 return -EINVAL;
97}
98
99static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
100{
101 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
102 u32 pmnc;
103
104 /* Disable all counters */
105 __raw_writel(PPMU_CCNT_MASK |
106 PPMU_PMCNT0_MASK |
107 PPMU_PMCNT1_MASK |
108 PPMU_PMCNT2_MASK |
109 PPMU_PMCNT3_MASK,
110 info->ppmu.base + PPMU_CNTENC);
111
112 /* Disable PPMU */
113 pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
114 pmnc &= ~PPMU_PMNC_ENABLE_MASK;
115 __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
116
117 return 0;
118}
119
120static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
121{
122 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
123 int id = exynos_ppmu_find_ppmu_id(edev);
124 u32 pmnc, cntens;
125
126 if (id < 0)
127 return id;
128
129 /* Enable specific counter */
130 cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS);
131 cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
132 __raw_writel(cntens, info->ppmu.base + PPMU_CNTENS);
133
134 /* Set the event of Read/Write data count */
135 __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT,
136 info->ppmu.base + PPMU_BEVTxSEL(id));
137
138 /* Reset cycle counter/performance counter and enable PPMU */
139 pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
140 pmnc &= ~(PPMU_PMNC_ENABLE_MASK
141 | PPMU_PMNC_COUNTER_RESET_MASK
142 | PPMU_PMNC_CC_RESET_MASK);
143 pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
144 pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
145 pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
146 __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
147
148 return 0;
149}
150
151static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
152 struct devfreq_event_data *edata)
153{
154 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
155 int id = exynos_ppmu_find_ppmu_id(edev);
156 u32 pmnc, cntenc;
157
158 if (id < 0)
159 return -EINVAL;
160
161 /* Disable PPMU */
162 pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC);
163 pmnc &= ~PPMU_PMNC_ENABLE_MASK;
164 __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC);
165
166 /* Read cycle count */
167 edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT);
168
169 /* Read performance count */
170 switch (id) {
171 case PPMU_PMNCNT0:
172 case PPMU_PMNCNT1:
173 case PPMU_PMNCNT2:
174 edata->load_count
175 = __raw_readl(info->ppmu.base + PPMU_PMNCT(id));
176 break;
177 case PPMU_PMNCNT3:
178 edata->load_count =
179 ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8)
180 | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW));
181 break;
182 default:
183 return -EINVAL;
184 }
185
186 /* Disable specific counter */
187 cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC);
188 cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
189 __raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC);
190
191 dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
192 edata->load_count, edata->total_count);
193
194 return 0;
195}
196
197static struct devfreq_event_ops exynos_ppmu_ops = {
198 .disable = exynos_ppmu_disable,
199 .set_event = exynos_ppmu_set_event,
200 .get_event = exynos_ppmu_get_event,
201};
202
203static int of_get_devfreq_events(struct device_node *np,
204 struct exynos_ppmu *info)
205{
206 struct devfreq_event_desc *desc;
207 struct device *dev = info->dev;
208 struct device_node *events_np, *node;
209 int i, j, count;
210
211 events_np = of_get_child_by_name(np, "events");
212 if (!events_np) {
213 dev_err(dev,
214 "failed to get child node of devfreq-event devices\n");
215 return -EINVAL;
216 }
217
218 count = of_get_child_count(events_np);
219 desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL);
220 if (!desc)
221 return -ENOMEM;
222 info->num_events = count;
223
224 j = 0;
225 for_each_child_of_node(events_np, node) {
226 for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
227 if (!ppmu_events[i].name)
228 continue;
229
230 if (!of_node_cmp(node->name, ppmu_events[i].name))
231 break;
232 }
233
234 if (i == ARRAY_SIZE(ppmu_events)) {
235 dev_warn(dev,
236 "don't know how to configure events : %s\n",
237 node->name);
238 continue;
239 }
240
241 desc[j].ops = &exynos_ppmu_ops;
242 desc[j].driver_data = info;
243
244 of_property_read_string(node, "event-name", &desc[j].name);
245
246 j++;
247
248 of_node_put(node);
249 }
250 info->desc = desc;
251
252 of_node_put(events_np);
253
254 return 0;
255}
256
257static int exynos_ppmu_parse_dt(struct exynos_ppmu *info)
258{
259 struct device *dev = info->dev;
260 struct device_node *np = dev->of_node;
261 int ret = 0;
262
263 if (!np) {
264 dev_err(dev, "failed to find devicetree node\n");
265 return -EINVAL;
266 }
267
268 /* Maps the memory mapped IO to control PPMU register */
269 info->ppmu.base = of_iomap(np, 0);
270 if (IS_ERR_OR_NULL(info->ppmu.base)) {
271 dev_err(dev, "failed to map memory region\n");
272 return -ENOMEM;
273 }
274
275 info->ppmu.clk = devm_clk_get(dev, "ppmu");
276 if (IS_ERR(info->ppmu.clk)) {
277 info->ppmu.clk = NULL;
278 dev_warn(dev, "cannot get PPMU clock\n");
279 }
280
281 ret = of_get_devfreq_events(np, info);
282 if (ret < 0) {
283 dev_err(dev, "failed to parse exynos ppmu dt node\n");
284 goto err;
285 }
286
287 return 0;
288
289err:
290 iounmap(info->ppmu.base);
291
292 return ret;
293}
294
295static int exynos_ppmu_probe(struct platform_device *pdev)
296{
297 struct exynos_ppmu *info;
298 struct devfreq_event_dev **edev;
299 struct devfreq_event_desc *desc;
300 int i, ret = 0, size;
301
302 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
303 if (!info)
304 return -ENOMEM;
305
306 mutex_init(&info->lock);
307 info->dev = &pdev->dev;
308
309 /* Parse dt data to get resource */
310 ret = exynos_ppmu_parse_dt(info);
311 if (ret < 0) {
312 dev_err(&pdev->dev,
313 "failed to parse devicetree for resource\n");
314 return ret;
315 }
316 desc = info->desc;
317
318 size = sizeof(struct devfreq_event_dev *) * info->num_events;
319 info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
320 if (!info->edev) {
321 dev_err(&pdev->dev,
322 "failed to allocate memory devfreq-event devices\n");
323 return -ENOMEM;
324 }
325 edev = info->edev;
326 platform_set_drvdata(pdev, info);
327
328 for (i = 0; i < info->num_events; i++) {
329 edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
330 if (IS_ERR(edev)) {
331 ret = PTR_ERR(edev);
332 dev_err(&pdev->dev,
333 "failed to add devfreq-event device\n");
334 goto err;
335 }
336 }
337
338 clk_prepare_enable(info->ppmu.clk);
339
340 return 0;
341err:
342 iounmap(info->ppmu.base);
343
344 return ret;
345}
346
347static int exynos_ppmu_remove(struct platform_device *pdev)
348{
349 struct exynos_ppmu *info = platform_get_drvdata(pdev);
350
351 clk_disable_unprepare(info->ppmu.clk);
352 iounmap(info->ppmu.base);
353
354 return 0;
355}
356
357static struct of_device_id exynos_ppmu_id_match[] = {
358 { .compatible = "samsung,exynos-ppmu", },
359 { /* sentinel */ },
360};
361
362static struct platform_driver exynos_ppmu_driver = {
363 .probe = exynos_ppmu_probe,
364 .remove = exynos_ppmu_remove,
365 .driver = {
366 .name = "exynos-ppmu",
367 .of_match_table = exynos_ppmu_id_match,
368 },
369};
370module_platform_driver(exynos_ppmu_driver);
371
372MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
373MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
374MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/event/exynos-ppmu.h b/drivers/devfreq/event/exynos-ppmu.h
new file mode 100644
index 000000000000..4e831d48c138
--- /dev/null
+++ b/drivers/devfreq/event/exynos-ppmu.h
@@ -0,0 +1,93 @@
1/*
2 * exynos_ppmu.h - EXYNOS PPMU header file
3 *
4 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
12#ifndef __EXYNOS_PPMU_H__
13#define __EXYNOS_PPMU_H__
14
15enum ppmu_state {
16 PPMU_DISABLE = 0,
17 PPMU_ENABLE,
18};
19
20enum ppmu_counter {
21 PPMU_PMNCNT0 = 0,
22 PPMU_PMNCNT1,
23 PPMU_PMNCNT2,
24 PPMU_PMNCNT3,
25
26 PPMU_PMNCNT_MAX,
27};
28
29enum ppmu_event_type {
30 PPMU_RO_BUSY_CYCLE_CNT = 0x0,
31 PPMU_WO_BUSY_CYCLE_CNT = 0x1,
32 PPMU_RW_BUSY_CYCLE_CNT = 0x2,
33 PPMU_RO_REQUEST_CNT = 0x3,
34 PPMU_WO_REQUEST_CNT = 0x4,
35 PPMU_RO_DATA_CNT = 0x5,
36 PPMU_WO_DATA_CNT = 0x6,
37 PPMU_RO_LATENCY = 0x12,
38 PPMU_WO_LATENCY = 0x16,
39};
40
41enum ppmu_reg {
42 /* PPC control register */
43 PPMU_PMNC = 0x00,
44 PPMU_CNTENS = 0x10,
45 PPMU_CNTENC = 0x20,
46 PPMU_INTENS = 0x30,
47 PPMU_INTENC = 0x40,
48 PPMU_FLAG = 0x50,
49
50 /* Cycle Counter and Performance Event Counter Register */
51 PPMU_CCNT = 0x100,
52 PPMU_PMCNT0 = 0x110,
53 PPMU_PMCNT1 = 0x120,
54 PPMU_PMCNT2 = 0x130,
55 PPMU_PMCNT3_HIGH = 0x140,
56 PPMU_PMCNT3_LOW = 0x150,
57
58 /* Bus Event Generator */
59 PPMU_BEVT0SEL = 0x1000,
60 PPMU_BEVT1SEL = 0x1100,
61 PPMU_BEVT2SEL = 0x1200,
62 PPMU_BEVT3SEL = 0x1300,
63 PPMU_COUNTER_RESET = 0x1810,
64 PPMU_READ_OVERFLOW_CNT = 0x1810,
65 PPMU_READ_UNDERFLOW_CNT = 0x1814,
66 PPMU_WRITE_OVERFLOW_CNT = 0x1850,
67 PPMU_WRITE_UNDERFLOW_CNT = 0x1854,
68 PPMU_READ_PENDING_CNT = 0x1880,
69 PPMU_WRITE_PENDING_CNT = 0x1884
70};
71
72/* PMNC register */
73#define PPMU_PMNC_CC_RESET_SHIFT 2
74#define PPMU_PMNC_COUNTER_RESET_SHIFT 1
75#define PPMU_PMNC_ENABLE_SHIFT 0
76#define PPMU_PMNC_START_MODE_MASK BIT(16)
77#define PPMU_PMNC_CC_DIVIDER_MASK BIT(3)
78#define PPMU_PMNC_CC_RESET_MASK BIT(2)
79#define PPMU_PMNC_COUNTER_RESET_MASK BIT(1)
80#define PPMU_PMNC_ENABLE_MASK BIT(0)
81
82/* CNTENS/CNTENC/INTENS/INTENC/FLAG register */
83#define PPMU_CCNT_MASK BIT(31)
84#define PPMU_PMCNT3_MASK BIT(3)
85#define PPMU_PMCNT2_MASK BIT(2)
86#define PPMU_PMCNT1_MASK BIT(1)
87#define PPMU_PMCNT0_MASK BIT(0)
88
89/* PPMU_PMNCTx/PPMU_BETxSEL registers */
90#define PPMU_PMNCT(x) (PPMU_PMCNT0 + (0x10 * x))
91#define PPMU_BEVTxSEL(x) (PPMU_BEVT0SEL + (0x100 * x))
92
93#endif /* __EXYNOS_PPMU_H__ */