1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f262f28cSChanwoo Choi /*
391d7f3f8SKrzysztof Kozlowski  * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support
4f262f28cSChanwoo Choi  *
577fe46a3SChanwoo Choi  * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
6f262f28cSChanwoo Choi  * Author : Chanwoo Choi <cw00.choi@samsung.com>
7f262f28cSChanwoo Choi  *
8f262f28cSChanwoo Choi  * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c
9f262f28cSChanwoo Choi  */
10f262f28cSChanwoo Choi 
11f262f28cSChanwoo Choi #include <linux/clk.h>
12f262f28cSChanwoo Choi #include <linux/io.h>
13f262f28cSChanwoo Choi #include <linux/kernel.h>
14f262f28cSChanwoo Choi #include <linux/module.h>
15f262f28cSChanwoo Choi #include <linux/of_address.h>
160ae9c321SLukasz Luba #include <linux/of_device.h>
17f262f28cSChanwoo Choi #include <linux/platform_device.h>
182a3ea647SChanwoo Choi #include <linux/regmap.h>
19f262f28cSChanwoo Choi #include <linux/suspend.h>
20f262f28cSChanwoo Choi #include <linux/devfreq-event.h>
21f262f28cSChanwoo Choi 
22f262f28cSChanwoo Choi #include "exynos-ppmu.h"
23f262f28cSChanwoo Choi 
240ae9c321SLukasz Luba enum exynos_ppmu_type {
250ae9c321SLukasz Luba 	EXYNOS_TYPE_PPMU,
260ae9c321SLukasz Luba 	EXYNOS_TYPE_PPMU_V2,
270ae9c321SLukasz Luba };
280ae9c321SLukasz Luba 
29f262f28cSChanwoo Choi struct exynos_ppmu_data {
30f262f28cSChanwoo Choi 	struct clk *clk;
31f262f28cSChanwoo Choi };
32f262f28cSChanwoo Choi 
33f262f28cSChanwoo Choi struct exynos_ppmu {
34f262f28cSChanwoo Choi 	struct devfreq_event_dev **edev;
35f262f28cSChanwoo Choi 	struct devfreq_event_desc *desc;
36f262f28cSChanwoo Choi 	unsigned int num_events;
37f262f28cSChanwoo Choi 
38f262f28cSChanwoo Choi 	struct device *dev;
392a3ea647SChanwoo Choi 	struct regmap *regmap;
40f262f28cSChanwoo Choi 
41f262f28cSChanwoo Choi 	struct exynos_ppmu_data ppmu;
420ae9c321SLukasz Luba 	enum exynos_ppmu_type ppmu_type;
43f262f28cSChanwoo Choi };
44f262f28cSChanwoo Choi 
45f262f28cSChanwoo Choi #define PPMU_EVENT(name)			\
46f262f28cSChanwoo Choi 	{ "ppmu-event0-"#name, PPMU_PMNCNT0 },	\
47f262f28cSChanwoo Choi 	{ "ppmu-event1-"#name, PPMU_PMNCNT1 },	\
48f262f28cSChanwoo Choi 	{ "ppmu-event2-"#name, PPMU_PMNCNT2 },	\
49f262f28cSChanwoo Choi 	{ "ppmu-event3-"#name, PPMU_PMNCNT3 }
50f262f28cSChanwoo Choi 
516b1355f9SKrzysztof Kozlowski static struct __exynos_ppmu_events {
52f262f28cSChanwoo Choi 	char *name;
53f262f28cSChanwoo Choi 	int id;
54f262f28cSChanwoo Choi } ppmu_events[] = {
55f262f28cSChanwoo Choi 	/* For Exynos3250, Exynos4 and Exynos5260 */
56f262f28cSChanwoo Choi 	PPMU_EVENT(g3d),
57f262f28cSChanwoo Choi 	PPMU_EVENT(fsys),
58f262f28cSChanwoo Choi 
59f262f28cSChanwoo Choi 	/* For Exynos4 SoCs and Exynos3250 */
60f262f28cSChanwoo Choi 	PPMU_EVENT(dmc0),
61f262f28cSChanwoo Choi 	PPMU_EVENT(dmc1),
62f262f28cSChanwoo Choi 	PPMU_EVENT(cpu),
63f262f28cSChanwoo Choi 	PPMU_EVENT(rightbus),
64f262f28cSChanwoo Choi 	PPMU_EVENT(leftbus),
65f262f28cSChanwoo Choi 	PPMU_EVENT(lcd0),
66f262f28cSChanwoo Choi 	PPMU_EVENT(camif),
67f262f28cSChanwoo Choi 
68f262f28cSChanwoo Choi 	/* Only for Exynos3250 and Exynos5260 */
69f262f28cSChanwoo Choi 	PPMU_EVENT(mfc),
70f262f28cSChanwoo Choi 
71f262f28cSChanwoo Choi 	/* Only for Exynos4 SoCs */
72f262f28cSChanwoo Choi 	PPMU_EVENT(mfc-left),
73f262f28cSChanwoo Choi 	PPMU_EVENT(mfc-right),
74f262f28cSChanwoo Choi 
75f262f28cSChanwoo Choi 	/* Only for Exynos5260 SoCs */
76f262f28cSChanwoo Choi 	PPMU_EVENT(drex0-s0),
77f262f28cSChanwoo Choi 	PPMU_EVENT(drex0-s1),
78f262f28cSChanwoo Choi 	PPMU_EVENT(drex1-s0),
79f262f28cSChanwoo Choi 	PPMU_EVENT(drex1-s1),
80f262f28cSChanwoo Choi 	PPMU_EVENT(eagle),
81f262f28cSChanwoo Choi 	PPMU_EVENT(kfc),
82f262f28cSChanwoo Choi 	PPMU_EVENT(isp),
83f262f28cSChanwoo Choi 	PPMU_EVENT(fimc),
84f262f28cSChanwoo Choi 	PPMU_EVENT(gscl),
85f262f28cSChanwoo Choi 	PPMU_EVENT(mscl),
86f262f28cSChanwoo Choi 	PPMU_EVENT(fimd0x),
87f262f28cSChanwoo Choi 	PPMU_EVENT(fimd1x),
8877fe46a3SChanwoo Choi 
8977fe46a3SChanwoo Choi 	/* Only for Exynos5433 SoCs */
9077fe46a3SChanwoo Choi 	PPMU_EVENT(d0-cpu),
9177fe46a3SChanwoo Choi 	PPMU_EVENT(d0-general),
9277fe46a3SChanwoo Choi 	PPMU_EVENT(d0-rt),
9377fe46a3SChanwoo Choi 	PPMU_EVENT(d1-cpu),
9477fe46a3SChanwoo Choi 	PPMU_EVENT(d1-general),
9577fe46a3SChanwoo Choi 	PPMU_EVENT(d1-rt),
965f866963SLukasz Luba 
9728d7f0f3SKrzysztof Kozlowski 	/* For Exynos5422 SoC, deprecated (backwards compatible) */
985f866963SLukasz Luba 	PPMU_EVENT(dmc0_0),
995f866963SLukasz Luba 	PPMU_EVENT(dmc0_1),
1005f866963SLukasz Luba 	PPMU_EVENT(dmc1_0),
1015f866963SLukasz Luba 	PPMU_EVENT(dmc1_1),
10228d7f0f3SKrzysztof Kozlowski 	/* For Exynos5422 SoC */
10328d7f0f3SKrzysztof Kozlowski 	PPMU_EVENT(dmc0-0),
10428d7f0f3SKrzysztof Kozlowski 	PPMU_EVENT(dmc0-1),
10528d7f0f3SKrzysztof Kozlowski 	PPMU_EVENT(dmc1-0),
10628d7f0f3SKrzysztof Kozlowski 	PPMU_EVENT(dmc1-1),
107f262f28cSChanwoo Choi };
108f262f28cSChanwoo Choi 
__exynos_ppmu_find_ppmu_id(const char * edev_name)109d4556f5eSArnd Bergmann static int __exynos_ppmu_find_ppmu_id(const char *edev_name)
110f262f28cSChanwoo Choi {
111f262f28cSChanwoo Choi 	int i;
112f262f28cSChanwoo Choi 
113f262f28cSChanwoo Choi 	for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
114d4556f5eSArnd Bergmann 		if (!strcmp(edev_name, ppmu_events[i].name))
115f262f28cSChanwoo Choi 			return ppmu_events[i].id;
116f262f28cSChanwoo Choi 
117f262f28cSChanwoo Choi 	return -EINVAL;
118f262f28cSChanwoo Choi }
119f262f28cSChanwoo Choi 
exynos_ppmu_find_ppmu_id(struct devfreq_event_dev * edev)120d4556f5eSArnd Bergmann static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
121d4556f5eSArnd Bergmann {
122d4556f5eSArnd Bergmann 	return __exynos_ppmu_find_ppmu_id(edev->desc->name);
123d4556f5eSArnd Bergmann }
124d4556f5eSArnd Bergmann 
12577fe46a3SChanwoo Choi /*
12677fe46a3SChanwoo Choi  * The devfreq-event ops structure for PPMU v1.1
12777fe46a3SChanwoo Choi  */
exynos_ppmu_disable(struct devfreq_event_dev * edev)128f262f28cSChanwoo Choi static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
129f262f28cSChanwoo Choi {
130f262f28cSChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
1312a3ea647SChanwoo Choi 	int ret;
132f262f28cSChanwoo Choi 	u32 pmnc;
133f262f28cSChanwoo Choi 
134f262f28cSChanwoo Choi 	/* Disable all counters */
1352a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_CNTENC,
1362a3ea647SChanwoo Choi 				PPMU_CCNT_MASK |
137f262f28cSChanwoo Choi 				PPMU_PMCNT0_MASK |
138f262f28cSChanwoo Choi 				PPMU_PMCNT1_MASK |
139f262f28cSChanwoo Choi 				PPMU_PMCNT2_MASK |
1402a3ea647SChanwoo Choi 				PPMU_PMCNT3_MASK);
1412a3ea647SChanwoo Choi 	if (ret < 0)
1422a3ea647SChanwoo Choi 		return ret;
143f262f28cSChanwoo Choi 
144f262f28cSChanwoo Choi 	/* Disable PPMU */
1452a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
1462a3ea647SChanwoo Choi 	if (ret < 0)
1472a3ea647SChanwoo Choi 		return ret;
1482a3ea647SChanwoo Choi 
149f262f28cSChanwoo Choi 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
1502a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
1512a3ea647SChanwoo Choi 	if (ret < 0)
1522a3ea647SChanwoo Choi 		return ret;
153f262f28cSChanwoo Choi 
154f262f28cSChanwoo Choi 	return 0;
155f262f28cSChanwoo Choi }
156f262f28cSChanwoo Choi 
exynos_ppmu_set_event(struct devfreq_event_dev * edev)157f262f28cSChanwoo Choi static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
158f262f28cSChanwoo Choi {
159f262f28cSChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
160f262f28cSChanwoo Choi 	int id = exynos_ppmu_find_ppmu_id(edev);
1612a3ea647SChanwoo Choi 	int ret;
162f262f28cSChanwoo Choi 	u32 pmnc, cntens;
163f262f28cSChanwoo Choi 
164f262f28cSChanwoo Choi 	if (id < 0)
165f262f28cSChanwoo Choi 		return id;
166f262f28cSChanwoo Choi 
167f262f28cSChanwoo Choi 	/* Enable specific counter */
1682a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
1692a3ea647SChanwoo Choi 	if (ret < 0)
1702a3ea647SChanwoo Choi 		return ret;
1712a3ea647SChanwoo Choi 
172f262f28cSChanwoo Choi 	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
1732a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
1742a3ea647SChanwoo Choi 	if (ret < 0)
1752a3ea647SChanwoo Choi 		return ret;
176f262f28cSChanwoo Choi 
1771dd62c66SLukasz Luba 	/* Set the event of proper data type monitoring */
1782a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
1791dd62c66SLukasz Luba 			   edev->desc->event_type);
1802a3ea647SChanwoo Choi 	if (ret < 0)
1812a3ea647SChanwoo Choi 		return ret;
182f262f28cSChanwoo Choi 
183f262f28cSChanwoo Choi 	/* Reset cycle counter/performance counter and enable PPMU */
1842a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
1852a3ea647SChanwoo Choi 	if (ret < 0)
1862a3ea647SChanwoo Choi 		return ret;
1872a3ea647SChanwoo Choi 
188f262f28cSChanwoo Choi 	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
189f262f28cSChanwoo Choi 			| PPMU_PMNC_COUNTER_RESET_MASK
190f262f28cSChanwoo Choi 			| PPMU_PMNC_CC_RESET_MASK);
191f262f28cSChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
192f262f28cSChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
193f262f28cSChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
1942a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
1952a3ea647SChanwoo Choi 	if (ret < 0)
1962a3ea647SChanwoo Choi 		return ret;
197f262f28cSChanwoo Choi 
198f262f28cSChanwoo Choi 	return 0;
199f262f28cSChanwoo Choi }
200f262f28cSChanwoo Choi 
exynos_ppmu_get_event(struct devfreq_event_dev * edev,struct devfreq_event_data * edata)201f262f28cSChanwoo Choi static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
202f262f28cSChanwoo Choi 				struct devfreq_event_data *edata)
203f262f28cSChanwoo Choi {
204f262f28cSChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
205f262f28cSChanwoo Choi 	int id = exynos_ppmu_find_ppmu_id(edev);
2062a3ea647SChanwoo Choi 	unsigned int total_count, load_count;
2072a3ea647SChanwoo Choi 	unsigned int pmcnt3_high, pmcnt3_low;
2082a3ea647SChanwoo Choi 	unsigned int pmnc, cntenc;
2092a3ea647SChanwoo Choi 	int ret;
210f262f28cSChanwoo Choi 
211f262f28cSChanwoo Choi 	if (id < 0)
212f262f28cSChanwoo Choi 		return -EINVAL;
213f262f28cSChanwoo Choi 
214f262f28cSChanwoo Choi 	/* Disable PPMU */
2152a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
2162a3ea647SChanwoo Choi 	if (ret < 0)
2172a3ea647SChanwoo Choi 		return ret;
2182a3ea647SChanwoo Choi 
219f262f28cSChanwoo Choi 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
2202a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
2212a3ea647SChanwoo Choi 	if (ret < 0)
2222a3ea647SChanwoo Choi 		return ret;
223f262f28cSChanwoo Choi 
224f262f28cSChanwoo Choi 	/* Read cycle count */
2252a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
2262a3ea647SChanwoo Choi 	if (ret < 0)
2272a3ea647SChanwoo Choi 		return ret;
2282a3ea647SChanwoo Choi 	edata->total_count = total_count;
229f262f28cSChanwoo Choi 
230f262f28cSChanwoo Choi 	/* Read performance count */
231f262f28cSChanwoo Choi 	switch (id) {
232f262f28cSChanwoo Choi 	case PPMU_PMNCNT0:
233f262f28cSChanwoo Choi 	case PPMU_PMNCNT1:
234f262f28cSChanwoo Choi 	case PPMU_PMNCNT2:
2352a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
2362a3ea647SChanwoo Choi 		if (ret < 0)
2372a3ea647SChanwoo Choi 			return ret;
2382a3ea647SChanwoo Choi 		edata->load_count = load_count;
239f262f28cSChanwoo Choi 		break;
240f262f28cSChanwoo Choi 	case PPMU_PMNCNT3:
2412a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
2422a3ea647SChanwoo Choi 		if (ret < 0)
2432a3ea647SChanwoo Choi 			return ret;
2442a3ea647SChanwoo Choi 
2452a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
2462a3ea647SChanwoo Choi 		if (ret < 0)
2472a3ea647SChanwoo Choi 			return ret;
2482a3ea647SChanwoo Choi 
2492a3ea647SChanwoo Choi 		edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
250f262f28cSChanwoo Choi 		break;
251f262f28cSChanwoo Choi 	default:
252f262f28cSChanwoo Choi 		return -EINVAL;
253f262f28cSChanwoo Choi 	}
254f262f28cSChanwoo Choi 
255f262f28cSChanwoo Choi 	/* Disable specific counter */
2562a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
2572a3ea647SChanwoo Choi 	if (ret < 0)
2582a3ea647SChanwoo Choi 		return ret;
2592a3ea647SChanwoo Choi 
260f262f28cSChanwoo Choi 	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
2612a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
2622a3ea647SChanwoo Choi 	if (ret < 0)
2632a3ea647SChanwoo Choi 		return ret;
264f262f28cSChanwoo Choi 
265f262f28cSChanwoo Choi 	dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
266f262f28cSChanwoo Choi 					edata->load_count, edata->total_count);
267f262f28cSChanwoo Choi 
268f262f28cSChanwoo Choi 	return 0;
269f262f28cSChanwoo Choi }
270f262f28cSChanwoo Choi 
2716f240fbcSChanwoo Choi static const struct devfreq_event_ops exynos_ppmu_ops = {
272f262f28cSChanwoo Choi 	.disable = exynos_ppmu_disable,
273f262f28cSChanwoo Choi 	.set_event = exynos_ppmu_set_event,
274f262f28cSChanwoo Choi 	.get_event = exynos_ppmu_get_event,
275f262f28cSChanwoo Choi };
276f262f28cSChanwoo Choi 
27777fe46a3SChanwoo Choi /*
27877fe46a3SChanwoo Choi  * The devfreq-event ops structure for PPMU v2.0
27977fe46a3SChanwoo Choi  */
exynos_ppmu_v2_disable(struct devfreq_event_dev * edev)28077fe46a3SChanwoo Choi static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
28177fe46a3SChanwoo Choi {
28277fe46a3SChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
2832a3ea647SChanwoo Choi 	int ret;
28477fe46a3SChanwoo Choi 	u32 pmnc, clear;
28577fe46a3SChanwoo Choi 
28677fe46a3SChanwoo Choi 	/* Disable all counters */
28777fe46a3SChanwoo Choi 	clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
28877fe46a3SChanwoo Choi 		| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
2892a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
2902a3ea647SChanwoo Choi 	if (ret < 0)
2912a3ea647SChanwoo Choi 		return ret;
29277fe46a3SChanwoo Choi 
2932a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
2942a3ea647SChanwoo Choi 	if (ret < 0)
2952a3ea647SChanwoo Choi 		return ret;
29677fe46a3SChanwoo Choi 
2972a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
2982a3ea647SChanwoo Choi 	if (ret < 0)
2992a3ea647SChanwoo Choi 		return ret;
3002a3ea647SChanwoo Choi 
3012a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
3022a3ea647SChanwoo Choi 	if (ret < 0)
3032a3ea647SChanwoo Choi 		return ret;
3042a3ea647SChanwoo Choi 
3052a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
3062a3ea647SChanwoo Choi 	if (ret < 0)
3072a3ea647SChanwoo Choi 		return ret;
3082a3ea647SChanwoo Choi 
3092a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
3102a3ea647SChanwoo Choi 	if (ret < 0)
3112a3ea647SChanwoo Choi 		return ret;
3122a3ea647SChanwoo Choi 
3132a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
3142a3ea647SChanwoo Choi 	if (ret < 0)
3152a3ea647SChanwoo Choi 		return ret;
3162a3ea647SChanwoo Choi 
3172a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
3182a3ea647SChanwoo Choi 	if (ret < 0)
3192a3ea647SChanwoo Choi 		return ret;
3202a3ea647SChanwoo Choi 
3212a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
3222a3ea647SChanwoo Choi 	if (ret < 0)
3232a3ea647SChanwoo Choi 		return ret;
3242a3ea647SChanwoo Choi 
3252a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
3262a3ea647SChanwoo Choi 	if (ret < 0)
3272a3ea647SChanwoo Choi 		return ret;
3282a3ea647SChanwoo Choi 
3292a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
3302a3ea647SChanwoo Choi 	if (ret < 0)
3312a3ea647SChanwoo Choi 		return ret;
3322a3ea647SChanwoo Choi 
3332a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
3342a3ea647SChanwoo Choi 	if (ret < 0)
3352a3ea647SChanwoo Choi 		return ret;
3362a3ea647SChanwoo Choi 
3372a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
3382a3ea647SChanwoo Choi 	if (ret < 0)
3392a3ea647SChanwoo Choi 		return ret;
3402a3ea647SChanwoo Choi 
3412a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
3422a3ea647SChanwoo Choi 	if (ret < 0)
3432a3ea647SChanwoo Choi 		return ret;
3442a3ea647SChanwoo Choi 
3452a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
3462a3ea647SChanwoo Choi 	if (ret < 0)
3472a3ea647SChanwoo Choi 		return ret;
3482a3ea647SChanwoo Choi 
3492a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
3502a3ea647SChanwoo Choi 	if (ret < 0)
3512a3ea647SChanwoo Choi 		return ret;
3522a3ea647SChanwoo Choi 
3532a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
3542a3ea647SChanwoo Choi 	if (ret < 0)
3552a3ea647SChanwoo Choi 		return ret;
3562a3ea647SChanwoo Choi 
3572a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
3582a3ea647SChanwoo Choi 	if (ret < 0)
3592a3ea647SChanwoo Choi 		return ret;
36077fe46a3SChanwoo Choi 
36177fe46a3SChanwoo Choi 	/* Disable PPMU */
3622a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
3632a3ea647SChanwoo Choi 	if (ret < 0)
3642a3ea647SChanwoo Choi 		return ret;
3652a3ea647SChanwoo Choi 
36677fe46a3SChanwoo Choi 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
3672a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
3682a3ea647SChanwoo Choi 	if (ret < 0)
3692a3ea647SChanwoo Choi 		return ret;
37077fe46a3SChanwoo Choi 
37177fe46a3SChanwoo Choi 	return 0;
37277fe46a3SChanwoo Choi }
37377fe46a3SChanwoo Choi 
exynos_ppmu_v2_set_event(struct devfreq_event_dev * edev)37477fe46a3SChanwoo Choi static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
37577fe46a3SChanwoo Choi {
37677fe46a3SChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
3772a3ea647SChanwoo Choi 	unsigned int pmnc, cntens;
37877fe46a3SChanwoo Choi 	int id = exynos_ppmu_find_ppmu_id(edev);
3792a3ea647SChanwoo Choi 	int ret;
38077fe46a3SChanwoo Choi 
38177fe46a3SChanwoo Choi 	/* Enable all counters */
3822a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
3832a3ea647SChanwoo Choi 	if (ret < 0)
3842a3ea647SChanwoo Choi 		return ret;
3852a3ea647SChanwoo Choi 
38677fe46a3SChanwoo Choi 	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
3872a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
3882a3ea647SChanwoo Choi 	if (ret < 0)
3892a3ea647SChanwoo Choi 		return ret;
39077fe46a3SChanwoo Choi 
3911dd62c66SLukasz Luba 	/* Set the event of proper data type monitoring */
3922a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
3931dd62c66SLukasz Luba 			   edev->desc->event_type);
3942a3ea647SChanwoo Choi 	if (ret < 0)
3952a3ea647SChanwoo Choi 		return ret;
39677fe46a3SChanwoo Choi 
39777fe46a3SChanwoo Choi 	/* Reset cycle counter/performance counter and enable PPMU */
3982a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
3992a3ea647SChanwoo Choi 	if (ret < 0)
4002a3ea647SChanwoo Choi 		return ret;
4012a3ea647SChanwoo Choi 
40277fe46a3SChanwoo Choi 	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
40377fe46a3SChanwoo Choi 			| PPMU_PMNC_COUNTER_RESET_MASK
40477fe46a3SChanwoo Choi 			| PPMU_PMNC_CC_RESET_MASK
40577fe46a3SChanwoo Choi 			| PPMU_PMNC_CC_DIVIDER_MASK
40677fe46a3SChanwoo Choi 			| PPMU_V2_PMNC_START_MODE_MASK);
40777fe46a3SChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
40877fe46a3SChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
40977fe46a3SChanwoo Choi 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
41077fe46a3SChanwoo Choi 	pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
4112a3ea647SChanwoo Choi 
4122a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
4132a3ea647SChanwoo Choi 	if (ret < 0)
4142a3ea647SChanwoo Choi 		return ret;
41577fe46a3SChanwoo Choi 
41677fe46a3SChanwoo Choi 	return 0;
41777fe46a3SChanwoo Choi }
41877fe46a3SChanwoo Choi 
exynos_ppmu_v2_get_event(struct devfreq_event_dev * edev,struct devfreq_event_data * edata)41977fe46a3SChanwoo Choi static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
42077fe46a3SChanwoo Choi 				    struct devfreq_event_data *edata)
42177fe46a3SChanwoo Choi {
42277fe46a3SChanwoo Choi 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
42377fe46a3SChanwoo Choi 	int id = exynos_ppmu_find_ppmu_id(edev);
4242a3ea647SChanwoo Choi 	int ret;
4252a3ea647SChanwoo Choi 	unsigned int pmnc, cntenc;
4262a3ea647SChanwoo Choi 	unsigned int pmcnt_high, pmcnt_low;
4272a3ea647SChanwoo Choi 	unsigned int total_count, count;
4282a3ea647SChanwoo Choi 	unsigned long load_count = 0;
42977fe46a3SChanwoo Choi 
43077fe46a3SChanwoo Choi 	/* Disable PPMU */
4312a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
4322a3ea647SChanwoo Choi 	if (ret < 0)
4332a3ea647SChanwoo Choi 		return ret;
4342a3ea647SChanwoo Choi 
43577fe46a3SChanwoo Choi 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
4362a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
4372a3ea647SChanwoo Choi 	if (ret < 0)
4382a3ea647SChanwoo Choi 		return ret;
43977fe46a3SChanwoo Choi 
44077fe46a3SChanwoo Choi 	/* Read cycle count and performance count */
4412a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
4422a3ea647SChanwoo Choi 	if (ret < 0)
4432a3ea647SChanwoo Choi 		return ret;
4442a3ea647SChanwoo Choi 	edata->total_count = total_count;
44577fe46a3SChanwoo Choi 
44677fe46a3SChanwoo Choi 	switch (id) {
44777fe46a3SChanwoo Choi 	case PPMU_PMNCNT0:
44877fe46a3SChanwoo Choi 	case PPMU_PMNCNT1:
44977fe46a3SChanwoo Choi 	case PPMU_PMNCNT2:
4502a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
4512a3ea647SChanwoo Choi 		if (ret < 0)
4522a3ea647SChanwoo Choi 			return ret;
4532a3ea647SChanwoo Choi 		load_count = count;
45477fe46a3SChanwoo Choi 		break;
45577fe46a3SChanwoo Choi 	case PPMU_PMNCNT3:
4562a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
4572a3ea647SChanwoo Choi 						&pmcnt_high);
4582a3ea647SChanwoo Choi 		if (ret < 0)
4592a3ea647SChanwoo Choi 			return ret;
4602a3ea647SChanwoo Choi 
4612a3ea647SChanwoo Choi 		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
4622a3ea647SChanwoo Choi 		if (ret < 0)
4632a3ea647SChanwoo Choi 			return ret;
4642a3ea647SChanwoo Choi 
4652a3ea647SChanwoo Choi 		load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
46677fe46a3SChanwoo Choi 		break;
46777fe46a3SChanwoo Choi 	}
46877fe46a3SChanwoo Choi 	edata->load_count = load_count;
46977fe46a3SChanwoo Choi 
47077fe46a3SChanwoo Choi 	/* Disable all counters */
4712a3ea647SChanwoo Choi 	ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
4722a3ea647SChanwoo Choi 	if (ret < 0)
4732a3ea647SChanwoo Choi 		return 0;
4742a3ea647SChanwoo Choi 
47577fe46a3SChanwoo Choi 	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
4762a3ea647SChanwoo Choi 	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
4772a3ea647SChanwoo Choi 	if (ret < 0)
4782a3ea647SChanwoo Choi 		return ret;
47977fe46a3SChanwoo Choi 
48077fe46a3SChanwoo Choi 	dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
48177fe46a3SChanwoo Choi 					edata->load_count, edata->total_count);
48277fe46a3SChanwoo Choi 	return 0;
48377fe46a3SChanwoo Choi }
48477fe46a3SChanwoo Choi 
48577fe46a3SChanwoo Choi static const struct devfreq_event_ops exynos_ppmu_v2_ops = {
48677fe46a3SChanwoo Choi 	.disable = exynos_ppmu_v2_disable,
48777fe46a3SChanwoo Choi 	.set_event = exynos_ppmu_v2_set_event,
48877fe46a3SChanwoo Choi 	.get_event = exynos_ppmu_v2_get_event,
48977fe46a3SChanwoo Choi };
49077fe46a3SChanwoo Choi 
49177fe46a3SChanwoo Choi static const struct of_device_id exynos_ppmu_id_match[] = {
49277fe46a3SChanwoo Choi 	{
49377fe46a3SChanwoo Choi 		.compatible = "samsung,exynos-ppmu",
4940ae9c321SLukasz Luba 		.data = (void *)EXYNOS_TYPE_PPMU,
49577fe46a3SChanwoo Choi 	}, {
49677fe46a3SChanwoo Choi 		.compatible = "samsung,exynos-ppmu-v2",
4970ae9c321SLukasz Luba 		.data = (void *)EXYNOS_TYPE_PPMU_V2,
49877fe46a3SChanwoo Choi 	},
49977fe46a3SChanwoo Choi 	{ /* sentinel */ },
50077fe46a3SChanwoo Choi };
50129e477f2SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);
50277fe46a3SChanwoo Choi 
of_get_devfreq_events(struct device_node * np,struct exynos_ppmu * info)503f262f28cSChanwoo Choi static int of_get_devfreq_events(struct device_node *np,
504f262f28cSChanwoo Choi 				 struct exynos_ppmu *info)
505f262f28cSChanwoo Choi {
506f262f28cSChanwoo Choi 	struct devfreq_event_desc *desc;
507f262f28cSChanwoo Choi 	struct device *dev = info->dev;
508f262f28cSChanwoo Choi 	struct device_node *events_np, *node;
509f262f28cSChanwoo Choi 	int i, j, count;
5100ae9c321SLukasz Luba 	const struct of_device_id *of_id;
5111dd62c66SLukasz Luba 	int ret;
512f262f28cSChanwoo Choi 
513f262f28cSChanwoo Choi 	events_np = of_get_child_by_name(np, "events");
514f262f28cSChanwoo Choi 	if (!events_np) {
515f262f28cSChanwoo Choi 		dev_err(dev,
516f262f28cSChanwoo Choi 			"failed to get child node of devfreq-event devices\n");
517f262f28cSChanwoo Choi 		return -EINVAL;
518f262f28cSChanwoo Choi 	}
519f262f28cSChanwoo Choi 
520f262f28cSChanwoo Choi 	count = of_get_child_count(events_np);
521a86854d0SKees Cook 	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
522f44b7996SMiaoqian Lin 	if (!desc) {
523f44b7996SMiaoqian Lin 		of_node_put(events_np);
524f262f28cSChanwoo Choi 		return -ENOMEM;
525f44b7996SMiaoqian Lin 	}
526f262f28cSChanwoo Choi 	info->num_events = count;
527f262f28cSChanwoo Choi 
5280ae9c321SLukasz Luba 	of_id = of_match_device(exynos_ppmu_id_match, dev);
5290ae9c321SLukasz Luba 	if (of_id)
5300ae9c321SLukasz Luba 		info->ppmu_type = (enum exynos_ppmu_type)of_id->data;
531f44b7996SMiaoqian Lin 	else {
532f44b7996SMiaoqian Lin 		of_node_put(events_np);
5330ae9c321SLukasz Luba 		return -EINVAL;
534f44b7996SMiaoqian Lin 	}
5350ae9c321SLukasz Luba 
536f262f28cSChanwoo Choi 	j = 0;
537f262f28cSChanwoo Choi 	for_each_child_of_node(events_np, node) {
538f262f28cSChanwoo Choi 		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
539f262f28cSChanwoo Choi 			if (!ppmu_events[i].name)
540f262f28cSChanwoo Choi 				continue;
541f262f28cSChanwoo Choi 
5420d00a239SRob Herring 			if (of_node_name_eq(node, ppmu_events[i].name))
543f262f28cSChanwoo Choi 				break;
544f262f28cSChanwoo Choi 		}
545f262f28cSChanwoo Choi 
546f262f28cSChanwoo Choi 		if (i == ARRAY_SIZE(ppmu_events)) {
547f262f28cSChanwoo Choi 			dev_warn(dev,
548f037eb8cSRob Herring 				"don't know how to configure events : %pOFn\n",
549f037eb8cSRob Herring 				node);
550f262f28cSChanwoo Choi 			continue;
551f262f28cSChanwoo Choi 		}
552f262f28cSChanwoo Choi 
5530ae9c321SLukasz Luba 		switch (info->ppmu_type) {
5540ae9c321SLukasz Luba 		case EXYNOS_TYPE_PPMU:
5550ae9c321SLukasz Luba 			desc[j].ops = &exynos_ppmu_ops;
5560ae9c321SLukasz Luba 			break;
5570ae9c321SLukasz Luba 		case EXYNOS_TYPE_PPMU_V2:
5580ae9c321SLukasz Luba 			desc[j].ops = &exynos_ppmu_v2_ops;
5590ae9c321SLukasz Luba 			break;
5600ae9c321SLukasz Luba 		}
5610ae9c321SLukasz Luba 
562f262f28cSChanwoo Choi 		desc[j].driver_data = info;
563f262f28cSChanwoo Choi 
564f262f28cSChanwoo Choi 		of_property_read_string(node, "event-name", &desc[j].name);
5651dd62c66SLukasz Luba 		ret = of_property_read_u32(node, "event-data-type",
5661dd62c66SLukasz Luba 					   &desc[j].event_type);
5671dd62c66SLukasz Luba 		if (ret) {
5681dd62c66SLukasz Luba 			/* Set the event of proper data type counting.
5691dd62c66SLukasz Luba 			 * Check if the data type has been defined in DT,
5701dd62c66SLukasz Luba 			 * use default if not.
5711dd62c66SLukasz Luba 			 */
5721dd62c66SLukasz Luba 			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {
5731dd62c66SLukasz Luba 				/* Not all registers take the same value for
5741dd62c66SLukasz Luba 				 * read+write data count.
5751dd62c66SLukasz Luba 				 */
57614714135SKrzysztof Kozlowski 				switch (ppmu_events[i].id) {
5771dd62c66SLukasz Luba 				case PPMU_PMNCNT0:
5781dd62c66SLukasz Luba 				case PPMU_PMNCNT1:
5791dd62c66SLukasz Luba 				case PPMU_PMNCNT2:
5801dd62c66SLukasz Luba 					desc[j].event_type = PPMU_V2_RO_DATA_CNT
5811dd62c66SLukasz Luba 						| PPMU_V2_WO_DATA_CNT;
5821dd62c66SLukasz Luba 					break;
5831dd62c66SLukasz Luba 				case PPMU_PMNCNT3:
5841dd62c66SLukasz Luba 					desc[j].event_type =
5851dd62c66SLukasz Luba 						PPMU_V2_EVT3_RW_DATA_CNT;
5861dd62c66SLukasz Luba 					break;
5871dd62c66SLukasz Luba 				}
5881dd62c66SLukasz Luba 			} else {
5891dd62c66SLukasz Luba 				desc[j].event_type = PPMU_RO_DATA_CNT |
5901dd62c66SLukasz Luba 					PPMU_WO_DATA_CNT;
5911dd62c66SLukasz Luba 			}
5921dd62c66SLukasz Luba 		}
593f262f28cSChanwoo Choi 
594f262f28cSChanwoo Choi 		j++;
595f262f28cSChanwoo Choi 	}
596f262f28cSChanwoo Choi 	info->desc = desc;
597f262f28cSChanwoo Choi 
598f262f28cSChanwoo Choi 	of_node_put(events_np);
599f262f28cSChanwoo Choi 
600f262f28cSChanwoo Choi 	return 0;
601f262f28cSChanwoo Choi }
602f262f28cSChanwoo Choi 
6032a3ea647SChanwoo Choi static struct regmap_config exynos_ppmu_regmap_config = {
6042a3ea647SChanwoo Choi 	.reg_bits = 32,
6052a3ea647SChanwoo Choi 	.val_bits = 32,
6062a3ea647SChanwoo Choi 	.reg_stride = 4,
6072a3ea647SChanwoo Choi };
6082a3ea647SChanwoo Choi 
exynos_ppmu_parse_dt(struct platform_device * pdev,struct exynos_ppmu * info)6092a3ea647SChanwoo Choi static int exynos_ppmu_parse_dt(struct platform_device *pdev,
6102a3ea647SChanwoo Choi 				struct exynos_ppmu *info)
611f262f28cSChanwoo Choi {
612f262f28cSChanwoo Choi 	struct device *dev = info->dev;
613f262f28cSChanwoo Choi 	struct device_node *np = dev->of_node;
6142a3ea647SChanwoo Choi 	struct resource *res;
6152a3ea647SChanwoo Choi 	void __iomem *base;
616f262f28cSChanwoo Choi 	int ret = 0;
617f262f28cSChanwoo Choi 
618f262f28cSChanwoo Choi 	if (!np) {
619f262f28cSChanwoo Choi 		dev_err(dev, "failed to find devicetree node\n");
620f262f28cSChanwoo Choi 		return -EINVAL;
621f262f28cSChanwoo Choi 	}
622f262f28cSChanwoo Choi 
623f262f28cSChanwoo Choi 	/* Maps the memory mapped IO to control PPMU register */
624*376b1446SYang Li 	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
6252a3ea647SChanwoo Choi 	if (IS_ERR(base))
6262a3ea647SChanwoo Choi 		return PTR_ERR(base);
6272a3ea647SChanwoo Choi 
6282a3ea647SChanwoo Choi 	exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
6292a3ea647SChanwoo Choi 	info->regmap = devm_regmap_init_mmio(dev, base,
6302a3ea647SChanwoo Choi 					&exynos_ppmu_regmap_config);
6312a3ea647SChanwoo Choi 	if (IS_ERR(info->regmap)) {
6322a3ea647SChanwoo Choi 		dev_err(dev, "failed to initialize regmap\n");
6332a3ea647SChanwoo Choi 		return PTR_ERR(info->regmap);
634f262f28cSChanwoo Choi 	}
635f262f28cSChanwoo Choi 
636f262f28cSChanwoo Choi 	info->ppmu.clk = devm_clk_get(dev, "ppmu");
637f262f28cSChanwoo Choi 	if (IS_ERR(info->ppmu.clk)) {
638f262f28cSChanwoo Choi 		info->ppmu.clk = NULL;
639f262f28cSChanwoo Choi 		dev_warn(dev, "cannot get PPMU clock\n");
640f262f28cSChanwoo Choi 	}
641f262f28cSChanwoo Choi 
642f262f28cSChanwoo Choi 	ret = of_get_devfreq_events(np, info);
643f262f28cSChanwoo Choi 	if (ret < 0) {
644f262f28cSChanwoo Choi 		dev_err(dev, "failed to parse exynos ppmu dt node\n");
6452a3ea647SChanwoo Choi 		return ret;
646f262f28cSChanwoo Choi 	}
647f262f28cSChanwoo Choi 
648f262f28cSChanwoo Choi 	return 0;
649f262f28cSChanwoo Choi }
650f262f28cSChanwoo Choi 
exynos_ppmu_probe(struct platform_device * pdev)651f262f28cSChanwoo Choi static int exynos_ppmu_probe(struct platform_device *pdev)
652f262f28cSChanwoo Choi {
653f262f28cSChanwoo Choi 	struct exynos_ppmu *info;
654f262f28cSChanwoo Choi 	struct devfreq_event_dev **edev;
655f262f28cSChanwoo Choi 	struct devfreq_event_desc *desc;
656f262f28cSChanwoo Choi 	int i, ret = 0, size;
657f262f28cSChanwoo Choi 
658f262f28cSChanwoo Choi 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
659f262f28cSChanwoo Choi 	if (!info)
660f262f28cSChanwoo Choi 		return -ENOMEM;
661f262f28cSChanwoo Choi 
662f262f28cSChanwoo Choi 	info->dev = &pdev->dev;
663f262f28cSChanwoo Choi 
664f262f28cSChanwoo Choi 	/* Parse dt data to get resource */
6652a3ea647SChanwoo Choi 	ret = exynos_ppmu_parse_dt(pdev, info);
666f262f28cSChanwoo Choi 	if (ret < 0) {
667f262f28cSChanwoo Choi 		dev_err(&pdev->dev,
668f262f28cSChanwoo Choi 			"failed to parse devicetree for resource\n");
669f262f28cSChanwoo Choi 		return ret;
670f262f28cSChanwoo Choi 	}
671f262f28cSChanwoo Choi 	desc = info->desc;
672f262f28cSChanwoo Choi 
673f262f28cSChanwoo Choi 	size = sizeof(struct devfreq_event_dev *) * info->num_events;
674f262f28cSChanwoo Choi 	info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
67512ba2c65SMarkus Elfring 	if (!info->edev)
6762a3ea647SChanwoo Choi 		return -ENOMEM;
67712ba2c65SMarkus Elfring 
678f262f28cSChanwoo Choi 	edev = info->edev;
679f262f28cSChanwoo Choi 	platform_set_drvdata(pdev, info);
680f262f28cSChanwoo Choi 
681f262f28cSChanwoo Choi 	for (i = 0; i < info->num_events; i++) {
682f262f28cSChanwoo Choi 		edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
68304a695edSDan Carpenter 		if (IS_ERR(edev[i])) {
684f262f28cSChanwoo Choi 			dev_err(&pdev->dev,
685f262f28cSChanwoo Choi 				"failed to add devfreq-event device\n");
6862a3ea647SChanwoo Choi 			return PTR_ERR(edev[i]);
687f262f28cSChanwoo Choi 		}
688b0d75c08SChanwoo Choi 
689b0d75c08SChanwoo Choi 		pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
690b0d75c08SChanwoo Choi 			dev_name(&pdev->dev), desc[i].name);
691f262f28cSChanwoo Choi 	}
692f262f28cSChanwoo Choi 
69397a6ba5bSArvind Yadav 	ret = clk_prepare_enable(info->ppmu.clk);
69497a6ba5bSArvind Yadav 	if (ret) {
69597a6ba5bSArvind Yadav 		dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
69697a6ba5bSArvind Yadav 		return ret;
69797a6ba5bSArvind Yadav 	}
698f262f28cSChanwoo Choi 
699f262f28cSChanwoo Choi 	return 0;
700f262f28cSChanwoo Choi }
701f262f28cSChanwoo Choi 
exynos_ppmu_remove(struct platform_device * pdev)702f262f28cSChanwoo Choi static int exynos_ppmu_remove(struct platform_device *pdev)
703f262f28cSChanwoo Choi {
704f262f28cSChanwoo Choi 	struct exynos_ppmu *info = platform_get_drvdata(pdev);
705f262f28cSChanwoo Choi 
706f262f28cSChanwoo Choi 	clk_disable_unprepare(info->ppmu.clk);
707f262f28cSChanwoo Choi 
708f262f28cSChanwoo Choi 	return 0;
709f262f28cSChanwoo Choi }
710f262f28cSChanwoo Choi 
711f262f28cSChanwoo Choi static struct platform_driver exynos_ppmu_driver = {
712f262f28cSChanwoo Choi 	.probe	= exynos_ppmu_probe,
713f262f28cSChanwoo Choi 	.remove	= exynos_ppmu_remove,
714f262f28cSChanwoo Choi 	.driver = {
715f262f28cSChanwoo Choi 		.name	= "exynos-ppmu",
716f262f28cSChanwoo Choi 		.of_match_table = exynos_ppmu_id_match,
717f262f28cSChanwoo Choi 	},
718f262f28cSChanwoo Choi };
719f262f28cSChanwoo Choi module_platform_driver(exynos_ppmu_driver);
720f262f28cSChanwoo Choi 
721f262f28cSChanwoo Choi MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
722f262f28cSChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
723f262f28cSChanwoo Choi MODULE_LICENSE("GPL");
724