aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/devfreq/event
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/event
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/event')
-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
4 files changed, 494 insertions, 0 deletions
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__ */