aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2016-04-06 22:29:11 -0400
committerMyungJoo Ham <myungjoo.ham@samsung.com>2016-05-02 22:21:07 -0400
commit0179a913875a8b39eaf5b848c876439a3a4650af (patch)
tree12eb612a68452fa957cdf42003858c603e6e6bcf
parent490b864ba0724ac464234d6a326b0aa40b75afc7 (diff)
PM / devfreq: event: Add new Exynos NoC probe driver
This patch adds NoC (Network on Chip) Probe driver which provides the primitive values to get the performance data. The packets that the Network on Chip (NoC) probes detects are transported over the network infrastructure. Exynos542x bus has multiple NoC probes to provide bandwidth information about behavior of the SoC that you can use while analyzing system performance. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Tested-by: Markus Reichl <m.reichl@fivetechno.de> Tested-by: Anand Moon <linux.amoon@gmail.com> Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
-rw-r--r--Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt26
-rw-r--r--drivers/devfreq/event/Kconfig8
-rw-r--r--drivers/devfreq/event/Makefile2
-rw-r--r--drivers/devfreq/event/exynos-nocp.c304
-rw-r--r--drivers/devfreq/event/exynos-nocp.h78
5 files changed, 418 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt b/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt
new file mode 100644
index 000000000000..fd459f00aa5a
--- /dev/null
+++ b/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt
@@ -0,0 +1,26 @@
1
2* Samsung Exynos NoC (Network on Chip) Probe device
3
4The Samsung Exynos542x SoC has NoC (Network on Chip) Probe for NoC bus.
5NoC provides the primitive values to get the performance data. The packets
6that the Network on Chip (NoC) probes detects are transported over
7the network infrastructure to observer units. You can configure probes to
8capture packets with header or data on the data request response network,
9or as traffic debug or statistic collectors. Exynos542x bus has multiple
10NoC probes to provide bandwidth information about behavior of the SoC
11that you can use while analyzing system performance.
12
13Required properties:
14- compatible: Should be "samsung,exynos5420-nocp"
15- reg: physical base address of each NoC Probe and length of memory mapped region.
16
17Optional properties:
18- clock-names : the name of clock used by the NoC Probe, "nocp"
19- clocks : phandles for clock specified in "clock-names" property
20
21Example : NoC Probe nodes in Device Tree are listed below.
22
23 nocp_mem0_0: nocp@10CA1000 {
24 compatible = "samsung,exynos5420-nocp";
25 reg = <0x10CA1000 0x200>;
26 };
diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
index a11720affc31..1e8b4f469f38 100644
--- a/drivers/devfreq/event/Kconfig
+++ b/drivers/devfreq/event/Kconfig
@@ -13,6 +13,14 @@ menuconfig PM_DEVFREQ_EVENT
13 13
14if PM_DEVFREQ_EVENT 14if PM_DEVFREQ_EVENT
15 15
16config DEVFREQ_EVENT_EXYNOS_NOCP
17 bool "EXYNOS NoC (Network On Chip) Probe 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 NoC
22 (Network on Chip) Probe counters to measure the bandwidth of AXI bus.
23
16config DEVFREQ_EVENT_EXYNOS_PPMU 24config DEVFREQ_EVENT_EXYNOS_PPMU
17 bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" 25 bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
18 depends on ARCH_EXYNOS 26 depends on ARCH_EXYNOS
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
index be146ead79cf..3d6afd352253 100644
--- a/drivers/devfreq/event/Makefile
+++ b/drivers/devfreq/event/Makefile
@@ -1,2 +1,4 @@
1# Exynos DEVFREQ Event Drivers 1# Exynos DEVFREQ Event Drivers
2
3obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o
2obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o 4obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
diff --git a/drivers/devfreq/event/exynos-nocp.c b/drivers/devfreq/event/exynos-nocp.c
new file mode 100644
index 000000000000..6b6a5f310486
--- /dev/null
+++ b/drivers/devfreq/event/exynos-nocp.c
@@ -0,0 +1,304 @@
1/*
2 * exynos-nocp.c - EXYNOS NoC (Network On Chip) Probe support
3 *
4 * Copyright (c) 2016 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#include <linux/clk.h>
13#include <linux/module.h>
14#include <linux/devfreq-event.h>
15#include <linux/kernel.h>
16#include <linux/of_address.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19
20#include "exynos-nocp.h"
21
22struct exynos_nocp {
23 struct devfreq_event_dev *edev;
24 struct devfreq_event_desc desc;
25
26 struct device *dev;
27
28 struct regmap *regmap;
29 struct clk *clk;
30};
31
32/*
33 * The devfreq-event ops structure for nocp probe.
34 */
35static int exynos_nocp_set_event(struct devfreq_event_dev *edev)
36{
37 struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
38 int ret;
39
40 /* Disable NoC probe */
41 ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
42 NOCP_MAIN_CTL_STATEN_MASK, 0);
43 if (ret < 0) {
44 dev_err(nocp->dev, "failed to disable the NoC probe device\n");
45 return ret;
46 }
47
48 /* Set a statistics dump period to 0 */
49 ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0);
50 if (ret < 0)
51 goto out;
52
53 /* Set the IntEvent fields of *_SRC */
54 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC,
55 NOCP_CNT_SRC_INTEVENT_MASK,
56 NOCP_CNT_SRC_INTEVENT_BYTE_MASK);
57 if (ret < 0)
58 goto out;
59
60 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC,
61 NOCP_CNT_SRC_INTEVENT_MASK,
62 NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
63 if (ret < 0)
64 goto out;
65
66 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC,
67 NOCP_CNT_SRC_INTEVENT_MASK,
68 NOCP_CNT_SRC_INTEVENT_CYCLE_MASK);
69 if (ret < 0)
70 goto out;
71
72 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC,
73 NOCP_CNT_SRC_INTEVENT_MASK,
74 NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
75 if (ret < 0)
76 goto out;
77
78
79 /* Set an alarm with a max/min value of 0 to generate StatALARM */
80 ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0);
81 if (ret < 0)
82 goto out;
83
84 ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0);
85 if (ret < 0)
86 goto out;
87
88 /* Set AlarmMode */
89 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE,
90 NOCP_CNT_ALARM_MODE_MASK,
91 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
92 if (ret < 0)
93 goto out;
94
95 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE,
96 NOCP_CNT_ALARM_MODE_MASK,
97 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
98 if (ret < 0)
99 goto out;
100
101 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE,
102 NOCP_CNT_ALARM_MODE_MASK,
103 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
104 if (ret < 0)
105 goto out;
106
107 ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE,
108 NOCP_CNT_ALARM_MODE_MASK,
109 NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
110 if (ret < 0)
111 goto out;
112
113 /* Enable the measurements by setting AlarmEn and StatEn */
114 ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
115 NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK,
116 NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK);
117 if (ret < 0)
118 goto out;
119
120 /* Set GlobalEN */
121 ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL,
122 NOCP_CFG_CTL_GLOBALEN_MASK,
123 NOCP_CFG_CTL_GLOBALEN_MASK);
124 if (ret < 0)
125 goto out;
126
127 /* Enable NoC probe */
128 ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
129 NOCP_MAIN_CTL_STATEN_MASK,
130 NOCP_MAIN_CTL_STATEN_MASK);
131 if (ret < 0)
132 goto out;
133
134 return 0;
135
136out:
137 /* Reset NoC probe */
138 if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
139 NOCP_MAIN_CTL_STATEN_MASK, 0)) {
140 dev_err(nocp->dev, "Failed to reset NoC probe device\n");
141 }
142
143 return ret;
144}
145
146static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
147 struct devfreq_event_data *edata)
148{
149 struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
150 unsigned int counter[4];
151 int ret;
152
153 /* Read cycle count */
154 ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]);
155 if (ret < 0)
156 goto out;
157
158 ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]);
159 if (ret < 0)
160 goto out;
161
162 ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]);
163 if (ret < 0)
164 goto out;
165
166 ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]);
167 if (ret < 0)
168 goto out;
169
170 edata->load_count = ((counter[1] << 16) | counter[0]);
171 edata->total_count = ((counter[3] << 16) | counter[2]);
172
173 dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
174 edata->load_count, edata->total_count);
175
176 return 0;
177
178out:
179 edata->load_count = 0;
180 edata->total_count = 0;
181
182 dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
183
184 return ret;
185}
186
187static const struct devfreq_event_ops exynos_nocp_ops = {
188 .set_event = exynos_nocp_set_event,
189 .get_event = exynos_nocp_get_event,
190};
191
192static const struct of_device_id exynos_nocp_id_match[] = {
193 { .compatible = "samsung,exynos5420-nocp", },
194 { /* sentinel */ },
195};
196
197static struct regmap_config exynos_nocp_regmap_config = {
198 .reg_bits = 32,
199 .val_bits = 32,
200 .reg_stride = 4,
201 .max_register = NOCP_COUNTERS_3_VAL,
202};
203
204static int exynos_nocp_parse_dt(struct platform_device *pdev,
205 struct exynos_nocp *nocp)
206{
207 struct device *dev = nocp->dev;
208 struct device_node *np = dev->of_node;
209 struct resource *res;
210 void __iomem *base;
211
212 if (!np) {
213 dev_err(dev, "failed to find devicetree node\n");
214 return -EINVAL;
215 }
216
217 nocp->clk = devm_clk_get(dev, "nocp");
218 if (IS_ERR(nocp->clk))
219 nocp->clk = NULL;
220
221 /* Maps the memory mapped IO to control nocp register */
222 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
223 if (IS_ERR(res))
224 return PTR_ERR(res);
225
226 base = devm_ioremap_resource(dev, res);
227 if (IS_ERR(base))
228 return PTR_ERR(base);
229
230 exynos_nocp_regmap_config.max_register = resource_size(res) - 4;
231
232 nocp->regmap = devm_regmap_init_mmio(dev, base,
233 &exynos_nocp_regmap_config);
234 if (IS_ERR(nocp->regmap)) {
235 dev_err(dev, "failed to initialize regmap\n");
236 return PTR_ERR(nocp->regmap);
237 }
238
239 return 0;
240}
241
242static int exynos_nocp_probe(struct platform_device *pdev)
243{
244 struct device *dev = &pdev->dev;
245 struct device_node *np = dev->of_node;
246 struct exynos_nocp *nocp;
247 int ret;
248
249 nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);
250 if (!nocp)
251 return -ENOMEM;
252
253 nocp->dev = &pdev->dev;
254
255 /* Parse dt data to get resource */
256 ret = exynos_nocp_parse_dt(pdev, nocp);
257 if (ret < 0) {
258 dev_err(&pdev->dev,
259 "failed to parse devicetree for resource\n");
260 return ret;
261 }
262
263 /* Add devfreq-event device to measure the bandwidth of NoC */
264 nocp->desc.ops = &exynos_nocp_ops;
265 nocp->desc.driver_data = nocp;
266 nocp->desc.name = np->full_name;
267 nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);
268 if (IS_ERR(nocp->edev)) {
269 dev_err(&pdev->dev,
270 "failed to add devfreq-event device\n");
271 return PTR_ERR(nocp->edev);
272 }
273 platform_set_drvdata(pdev, nocp);
274
275 clk_prepare_enable(nocp->clk);
276
277 pr_info("exynos-nocp: new NoC Probe device registered: %s\n",
278 dev_name(dev));
279
280 return 0;
281}
282
283static int exynos_nocp_remove(struct platform_device *pdev)
284{
285 struct exynos_nocp *nocp = platform_get_drvdata(pdev);
286
287 clk_disable_unprepare(nocp->clk);
288
289 return 0;
290}
291
292static struct platform_driver exynos_nocp_driver = {
293 .probe = exynos_nocp_probe,
294 .remove = exynos_nocp_remove,
295 .driver = {
296 .name = "exynos-nocp",
297 .of_match_table = exynos_nocp_id_match,
298 },
299};
300module_platform_driver(exynos_nocp_driver);
301
302MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");
303MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
304MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/event/exynos-nocp.h b/drivers/devfreq/event/exynos-nocp.h
new file mode 100644
index 000000000000..28564db0edb8
--- /dev/null
+++ b/drivers/devfreq/event/exynos-nocp.h
@@ -0,0 +1,78 @@
1/*
2 * exynos-nocp.h - EXYNOS NoC (Network on Chip) Probe header file
3 *
4 * Copyright (c) 2016 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_NOCP_H__
13#define __EXYNOS_NOCP_H__
14
15enum nocp_reg {
16 NOCP_ID_REVISION_ID = 0x04,
17 NOCP_MAIN_CTL = 0x08,
18 NOCP_CFG_CTL = 0x0C,
19
20 NOCP_STAT_PERIOD = 0x24,
21 NOCP_STAT_GO = 0x28,
22 NOCP_STAT_ALARM_MIN = 0x2C,
23 NOCP_STAT_ALARM_MAX = 0x30,
24 NOCP_STAT_ALARM_STATUS = 0x34,
25 NOCP_STAT_ALARM_CLR = 0x38,
26
27 NOCP_COUNTERS_0_SRC = 0x138,
28 NOCP_COUNTERS_0_ALARM_MODE = 0x13C,
29 NOCP_COUNTERS_0_VAL = 0x140,
30
31 NOCP_COUNTERS_1_SRC = 0x14C,
32 NOCP_COUNTERS_1_ALARM_MODE = 0x150,
33 NOCP_COUNTERS_1_VAL = 0x154,
34
35 NOCP_COUNTERS_2_SRC = 0x160,
36 NOCP_COUNTERS_2_ALARM_MODE = 0x164,
37 NOCP_COUNTERS_2_VAL = 0x168,
38
39 NOCP_COUNTERS_3_SRC = 0x174,
40 NOCP_COUNTERS_3_ALARM_MODE = 0x178,
41 NOCP_COUNTERS_3_VAL = 0x17C,
42};
43
44/* NOCP_MAIN_CTL register */
45#define NOCP_MAIN_CTL_ERREN_MASK BIT(0)
46#define NOCP_MAIN_CTL_TRACEEN_MASK BIT(1)
47#define NOCP_MAIN_CTL_PAYLOADEN_MASK BIT(2)
48#define NOCP_MAIN_CTL_STATEN_MASK BIT(3)
49#define NOCP_MAIN_CTL_ALARMEN_MASK BIT(4)
50#define NOCP_MAIN_CTL_STATCONDDUMP_MASK BIT(5)
51#define NOCP_MAIN_CTL_INTRUSIVEMODE_MASK BIT(6)
52
53/* NOCP_CFG_CTL register */
54#define NOCP_CFG_CTL_GLOBALEN_MASK BIT(0)
55#define NOCP_CFG_CTL_ACTIVE_MASK BIT(1)
56
57/* NOCP_COUNTERS_x_SRC register */
58#define NOCP_CNT_SRC_INTEVENT_SHIFT 0
59#define NOCP_CNT_SRC_INTEVENT_MASK (0x1F << NOCP_CNT_SRC_INTEVENT_SHIFT)
60#define NOCP_CNT_SRC_INTEVENT_OFF_MASK (0x0 << NOCP_CNT_SRC_INTEVENT_SHIFT)
61#define NOCP_CNT_SRC_INTEVENT_CYCLE_MASK (0x1 << NOCP_CNT_SRC_INTEVENT_SHIFT)
62#define NOCP_CNT_SRC_INTEVENT_IDLE_MASK (0x2 << NOCP_CNT_SRC_INTEVENT_SHIFT)
63#define NOCP_CNT_SRC_INTEVENT_XFER_MASK (0x3 << NOCP_CNT_SRC_INTEVENT_SHIFT)
64#define NOCP_CNT_SRC_INTEVENT_BUSY_MASK (0x4 << NOCP_CNT_SRC_INTEVENT_SHIFT)
65#define NOCP_CNT_SRC_INTEVENT_WAIT_MASK (0x5 << NOCP_CNT_SRC_INTEVENT_SHIFT)
66#define NOCP_CNT_SRC_INTEVENT_PKT_MASK (0x6 << NOCP_CNT_SRC_INTEVENT_SHIFT)
67#define NOCP_CNT_SRC_INTEVENT_BYTE_MASK (0x8 << NOCP_CNT_SRC_INTEVENT_SHIFT)
68#define NOCP_CNT_SRC_INTEVENT_CHAIN_MASK (0x10 << NOCP_CNT_SRC_INTEVENT_SHIFT)
69
70/* NOCP_COUNTERS_x_ALARM_MODE register */
71#define NOCP_CNT_ALARM_MODE_SHIFT 0
72#define NOCP_CNT_ALARM_MODE_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT)
73#define NOCP_CNT_ALARM_MODE_OFF_MASK (0x0 << NOCP_CNT_ALARM_MODE_SHIFT)
74#define NOCP_CNT_ALARM_MODE_MIN_MASK (0x1 << NOCP_CNT_ALARM_MODE_SHIFT)
75#define NOCP_CNT_ALARM_MODE_MAX_MASK (0x2 << NOCP_CNT_ALARM_MODE_SHIFT)
76#define NOCP_CNT_ALARM_MODE_MIN_MAX_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT)
77
78#endif /* __EXYNOS_NOCP_H__ */