Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/devfreq/event/exynos-nocp.c
26428 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* exynos-nocp.c - Exynos NoC (Network On Chip) Probe support
4
*
5
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
6
* Author : Chanwoo Choi <[email protected]>
7
*/
8
9
#include <linux/clk.h>
10
#include <linux/module.h>
11
#include <linux/devfreq-event.h>
12
#include <linux/kernel.h>
13
#include <linux/of_address.h>
14
#include <linux/platform_device.h>
15
#include <linux/regmap.h>
16
17
#include "exynos-nocp.h"
18
19
struct exynos_nocp {
20
struct devfreq_event_dev *edev;
21
struct devfreq_event_desc desc;
22
23
struct device *dev;
24
25
struct regmap *regmap;
26
struct clk *clk;
27
};
28
29
/*
30
* The devfreq-event ops structure for nocp probe.
31
*/
32
static int exynos_nocp_set_event(struct devfreq_event_dev *edev)
33
{
34
struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
35
int ret;
36
37
/* Disable NoC probe */
38
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
39
NOCP_MAIN_CTL_STATEN_MASK, 0);
40
if (ret < 0) {
41
dev_err(nocp->dev, "failed to disable the NoC probe device\n");
42
return ret;
43
}
44
45
/* Set a statistics dump period to 0 */
46
ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0);
47
if (ret < 0)
48
goto out;
49
50
/* Set the IntEvent fields of *_SRC */
51
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC,
52
NOCP_CNT_SRC_INTEVENT_MASK,
53
NOCP_CNT_SRC_INTEVENT_BYTE_MASK);
54
if (ret < 0)
55
goto out;
56
57
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC,
58
NOCP_CNT_SRC_INTEVENT_MASK,
59
NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
60
if (ret < 0)
61
goto out;
62
63
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC,
64
NOCP_CNT_SRC_INTEVENT_MASK,
65
NOCP_CNT_SRC_INTEVENT_CYCLE_MASK);
66
if (ret < 0)
67
goto out;
68
69
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC,
70
NOCP_CNT_SRC_INTEVENT_MASK,
71
NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
72
if (ret < 0)
73
goto out;
74
75
76
/* Set an alarm with a max/min value of 0 to generate StatALARM */
77
ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0);
78
if (ret < 0)
79
goto out;
80
81
ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0);
82
if (ret < 0)
83
goto out;
84
85
/* Set AlarmMode */
86
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE,
87
NOCP_CNT_ALARM_MODE_MASK,
88
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
89
if (ret < 0)
90
goto out;
91
92
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE,
93
NOCP_CNT_ALARM_MODE_MASK,
94
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
95
if (ret < 0)
96
goto out;
97
98
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE,
99
NOCP_CNT_ALARM_MODE_MASK,
100
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
101
if (ret < 0)
102
goto out;
103
104
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE,
105
NOCP_CNT_ALARM_MODE_MASK,
106
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
107
if (ret < 0)
108
goto out;
109
110
/* Enable the measurements by setting AlarmEn and StatEn */
111
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
112
NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK,
113
NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK);
114
if (ret < 0)
115
goto out;
116
117
/* Set GlobalEN */
118
ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL,
119
NOCP_CFG_CTL_GLOBALEN_MASK,
120
NOCP_CFG_CTL_GLOBALEN_MASK);
121
if (ret < 0)
122
goto out;
123
124
/* Enable NoC probe */
125
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
126
NOCP_MAIN_CTL_STATEN_MASK,
127
NOCP_MAIN_CTL_STATEN_MASK);
128
if (ret < 0)
129
goto out;
130
131
return 0;
132
133
out:
134
/* Reset NoC probe */
135
if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
136
NOCP_MAIN_CTL_STATEN_MASK, 0)) {
137
dev_err(nocp->dev, "Failed to reset NoC probe device\n");
138
}
139
140
return ret;
141
}
142
143
static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
144
struct devfreq_event_data *edata)
145
{
146
struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
147
unsigned int counter[4];
148
int ret;
149
150
/* Read cycle count */
151
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]);
152
if (ret < 0)
153
goto out;
154
155
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]);
156
if (ret < 0)
157
goto out;
158
159
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]);
160
if (ret < 0)
161
goto out;
162
163
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]);
164
if (ret < 0)
165
goto out;
166
167
edata->load_count = ((counter[1] << 16) | counter[0]);
168
edata->total_count = ((counter[3] << 16) | counter[2]);
169
170
dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
171
edata->load_count, edata->total_count);
172
173
return 0;
174
175
out:
176
dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
177
178
return ret;
179
}
180
181
static const struct devfreq_event_ops exynos_nocp_ops = {
182
.set_event = exynos_nocp_set_event,
183
.get_event = exynos_nocp_get_event,
184
};
185
186
static const struct of_device_id exynos_nocp_id_match[] = {
187
{ .compatible = "samsung,exynos5420-nocp", },
188
{ /* sentinel */ },
189
};
190
MODULE_DEVICE_TABLE(of, exynos_nocp_id_match);
191
192
static struct regmap_config exynos_nocp_regmap_config = {
193
.reg_bits = 32,
194
.val_bits = 32,
195
.reg_stride = 4,
196
.max_register = NOCP_COUNTERS_3_VAL,
197
};
198
199
static int exynos_nocp_parse_dt(struct platform_device *pdev,
200
struct exynos_nocp *nocp)
201
{
202
struct device *dev = nocp->dev;
203
struct device_node *np = dev->of_node;
204
struct resource *res;
205
void __iomem *base;
206
207
if (!np) {
208
dev_err(dev, "failed to find devicetree node\n");
209
return -EINVAL;
210
}
211
212
nocp->clk = devm_clk_get(dev, "nocp");
213
if (IS_ERR(nocp->clk))
214
nocp->clk = NULL;
215
216
/* Maps the memory mapped IO to control nocp register */
217
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
218
if (IS_ERR(base))
219
return PTR_ERR(base);
220
221
exynos_nocp_regmap_config.max_register = resource_size(res) - 4;
222
223
nocp->regmap = devm_regmap_init_mmio(dev, base,
224
&exynos_nocp_regmap_config);
225
if (IS_ERR(nocp->regmap)) {
226
dev_err(dev, "failed to initialize regmap\n");
227
return PTR_ERR(nocp->regmap);
228
}
229
230
return 0;
231
}
232
233
static int exynos_nocp_probe(struct platform_device *pdev)
234
{
235
struct device *dev = &pdev->dev;
236
struct device_node *np = dev->of_node;
237
struct exynos_nocp *nocp;
238
int ret;
239
240
nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);
241
if (!nocp)
242
return -ENOMEM;
243
244
nocp->dev = &pdev->dev;
245
246
/* Parse dt data to get resource */
247
ret = exynos_nocp_parse_dt(pdev, nocp);
248
if (ret < 0) {
249
dev_err(&pdev->dev,
250
"failed to parse devicetree for resource\n");
251
return ret;
252
}
253
254
/* Add devfreq-event device to measure the bandwidth of NoC */
255
nocp->desc.ops = &exynos_nocp_ops;
256
nocp->desc.driver_data = nocp;
257
nocp->desc.name = np->full_name;
258
nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);
259
if (IS_ERR(nocp->edev)) {
260
dev_err(&pdev->dev,
261
"failed to add devfreq-event device\n");
262
return PTR_ERR(nocp->edev);
263
}
264
platform_set_drvdata(pdev, nocp);
265
266
ret = clk_prepare_enable(nocp->clk);
267
if (ret) {
268
dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
269
return ret;
270
}
271
272
pr_info("exynos-nocp: new NoC Probe device registered: %s\n",
273
dev_name(dev));
274
275
return 0;
276
}
277
278
static void exynos_nocp_remove(struct platform_device *pdev)
279
{
280
struct exynos_nocp *nocp = platform_get_drvdata(pdev);
281
282
clk_disable_unprepare(nocp->clk);
283
}
284
285
static struct platform_driver exynos_nocp_driver = {
286
.probe = exynos_nocp_probe,
287
.remove = exynos_nocp_remove,
288
.driver = {
289
.name = "exynos-nocp",
290
.of_match_table = exynos_nocp_id_match,
291
},
292
};
293
module_platform_driver(exynos_nocp_driver);
294
295
MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");
296
MODULE_AUTHOR("Chanwoo Choi <[email protected]>");
297
MODULE_LICENSE("GPL");
298
299