xref: /OK3568_Linux_fs/kernel/drivers/devfreq/event/exynos-ppmu.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun  * Author : Chanwoo Choi <cw00.choi@samsung.com>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/clk.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/of_address.h>
16*4882a593Smuzhiyun #include <linux/of_device.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/regmap.h>
19*4882a593Smuzhiyun #include <linux/suspend.h>
20*4882a593Smuzhiyun #include <linux/devfreq-event.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include "exynos-ppmu.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun enum exynos_ppmu_type {
25*4882a593Smuzhiyun 	EXYNOS_TYPE_PPMU,
26*4882a593Smuzhiyun 	EXYNOS_TYPE_PPMU_V2,
27*4882a593Smuzhiyun };
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun struct exynos_ppmu_data {
30*4882a593Smuzhiyun 	struct clk *clk;
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun struct exynos_ppmu {
34*4882a593Smuzhiyun 	struct devfreq_event_dev **edev;
35*4882a593Smuzhiyun 	struct devfreq_event_desc *desc;
36*4882a593Smuzhiyun 	unsigned int num_events;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	struct device *dev;
39*4882a593Smuzhiyun 	struct regmap *regmap;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	struct exynos_ppmu_data ppmu;
42*4882a593Smuzhiyun 	enum exynos_ppmu_type ppmu_type;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define PPMU_EVENT(name)			\
46*4882a593Smuzhiyun 	{ "ppmu-event0-"#name, PPMU_PMNCNT0 },	\
47*4882a593Smuzhiyun 	{ "ppmu-event1-"#name, PPMU_PMNCNT1 },	\
48*4882a593Smuzhiyun 	{ "ppmu-event2-"#name, PPMU_PMNCNT2 },	\
49*4882a593Smuzhiyun 	{ "ppmu-event3-"#name, PPMU_PMNCNT3 }
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static struct __exynos_ppmu_events {
52*4882a593Smuzhiyun 	char *name;
53*4882a593Smuzhiyun 	int id;
54*4882a593Smuzhiyun } ppmu_events[] = {
55*4882a593Smuzhiyun 	/* For Exynos3250, Exynos4 and Exynos5260 */
56*4882a593Smuzhiyun 	PPMU_EVENT(g3d),
57*4882a593Smuzhiyun 	PPMU_EVENT(fsys),
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	/* For Exynos4 SoCs and Exynos3250 */
60*4882a593Smuzhiyun 	PPMU_EVENT(dmc0),
61*4882a593Smuzhiyun 	PPMU_EVENT(dmc1),
62*4882a593Smuzhiyun 	PPMU_EVENT(cpu),
63*4882a593Smuzhiyun 	PPMU_EVENT(rightbus),
64*4882a593Smuzhiyun 	PPMU_EVENT(leftbus),
65*4882a593Smuzhiyun 	PPMU_EVENT(lcd0),
66*4882a593Smuzhiyun 	PPMU_EVENT(camif),
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	/* Only for Exynos3250 and Exynos5260 */
69*4882a593Smuzhiyun 	PPMU_EVENT(mfc),
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	/* Only for Exynos4 SoCs */
72*4882a593Smuzhiyun 	PPMU_EVENT(mfc-left),
73*4882a593Smuzhiyun 	PPMU_EVENT(mfc-right),
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	/* Only for Exynos5260 SoCs */
76*4882a593Smuzhiyun 	PPMU_EVENT(drex0-s0),
77*4882a593Smuzhiyun 	PPMU_EVENT(drex0-s1),
78*4882a593Smuzhiyun 	PPMU_EVENT(drex1-s0),
79*4882a593Smuzhiyun 	PPMU_EVENT(drex1-s1),
80*4882a593Smuzhiyun 	PPMU_EVENT(eagle),
81*4882a593Smuzhiyun 	PPMU_EVENT(kfc),
82*4882a593Smuzhiyun 	PPMU_EVENT(isp),
83*4882a593Smuzhiyun 	PPMU_EVENT(fimc),
84*4882a593Smuzhiyun 	PPMU_EVENT(gscl),
85*4882a593Smuzhiyun 	PPMU_EVENT(mscl),
86*4882a593Smuzhiyun 	PPMU_EVENT(fimd0x),
87*4882a593Smuzhiyun 	PPMU_EVENT(fimd1x),
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	/* Only for Exynos5433 SoCs */
90*4882a593Smuzhiyun 	PPMU_EVENT(d0-cpu),
91*4882a593Smuzhiyun 	PPMU_EVENT(d0-general),
92*4882a593Smuzhiyun 	PPMU_EVENT(d0-rt),
93*4882a593Smuzhiyun 	PPMU_EVENT(d1-cpu),
94*4882a593Smuzhiyun 	PPMU_EVENT(d1-general),
95*4882a593Smuzhiyun 	PPMU_EVENT(d1-rt),
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	/* For Exynos5422 SoC */
98*4882a593Smuzhiyun 	PPMU_EVENT(dmc0_0),
99*4882a593Smuzhiyun 	PPMU_EVENT(dmc0_1),
100*4882a593Smuzhiyun 	PPMU_EVENT(dmc1_0),
101*4882a593Smuzhiyun 	PPMU_EVENT(dmc1_1),
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun 
__exynos_ppmu_find_ppmu_id(const char * edev_name)104*4882a593Smuzhiyun static int __exynos_ppmu_find_ppmu_id(const char *edev_name)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	int i;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
109*4882a593Smuzhiyun 		if (!strcmp(edev_name, ppmu_events[i].name))
110*4882a593Smuzhiyun 			return ppmu_events[i].id;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	return -EINVAL;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
exynos_ppmu_find_ppmu_id(struct devfreq_event_dev * edev)115*4882a593Smuzhiyun static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	return __exynos_ppmu_find_ppmu_id(edev->desc->name);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun  * The devfreq-event ops structure for PPMU v1.1
122*4882a593Smuzhiyun  */
exynos_ppmu_disable(struct devfreq_event_dev * edev)123*4882a593Smuzhiyun static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
126*4882a593Smuzhiyun 	int ret;
127*4882a593Smuzhiyun 	u32 pmnc;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	/* Disable all counters */
130*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_CNTENC,
131*4882a593Smuzhiyun 				PPMU_CCNT_MASK |
132*4882a593Smuzhiyun 				PPMU_PMCNT0_MASK |
133*4882a593Smuzhiyun 				PPMU_PMCNT1_MASK |
134*4882a593Smuzhiyun 				PPMU_PMCNT2_MASK |
135*4882a593Smuzhiyun 				PPMU_PMCNT3_MASK);
136*4882a593Smuzhiyun 	if (ret < 0)
137*4882a593Smuzhiyun 		return ret;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	/* Disable PPMU */
140*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
141*4882a593Smuzhiyun 	if (ret < 0)
142*4882a593Smuzhiyun 		return ret;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
145*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
146*4882a593Smuzhiyun 	if (ret < 0)
147*4882a593Smuzhiyun 		return ret;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
exynos_ppmu_set_event(struct devfreq_event_dev * edev)152*4882a593Smuzhiyun static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
155*4882a593Smuzhiyun 	int id = exynos_ppmu_find_ppmu_id(edev);
156*4882a593Smuzhiyun 	int ret;
157*4882a593Smuzhiyun 	u32 pmnc, cntens;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (id < 0)
160*4882a593Smuzhiyun 		return id;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/* Enable specific counter */
163*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
164*4882a593Smuzhiyun 	if (ret < 0)
165*4882a593Smuzhiyun 		return ret;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
168*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
169*4882a593Smuzhiyun 	if (ret < 0)
170*4882a593Smuzhiyun 		return ret;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	/* Set the event of proper data type monitoring */
173*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
174*4882a593Smuzhiyun 			   edev->desc->event_type);
175*4882a593Smuzhiyun 	if (ret < 0)
176*4882a593Smuzhiyun 		return ret;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	/* Reset cycle counter/performance counter and enable PPMU */
179*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
180*4882a593Smuzhiyun 	if (ret < 0)
181*4882a593Smuzhiyun 		return ret;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
184*4882a593Smuzhiyun 			| PPMU_PMNC_COUNTER_RESET_MASK
185*4882a593Smuzhiyun 			| PPMU_PMNC_CC_RESET_MASK);
186*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
187*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
188*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
189*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
190*4882a593Smuzhiyun 	if (ret < 0)
191*4882a593Smuzhiyun 		return ret;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	return 0;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
exynos_ppmu_get_event(struct devfreq_event_dev * edev,struct devfreq_event_data * edata)196*4882a593Smuzhiyun static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
197*4882a593Smuzhiyun 				struct devfreq_event_data *edata)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
200*4882a593Smuzhiyun 	int id = exynos_ppmu_find_ppmu_id(edev);
201*4882a593Smuzhiyun 	unsigned int total_count, load_count;
202*4882a593Smuzhiyun 	unsigned int pmcnt3_high, pmcnt3_low;
203*4882a593Smuzhiyun 	unsigned int pmnc, cntenc;
204*4882a593Smuzhiyun 	int ret;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	if (id < 0)
207*4882a593Smuzhiyun 		return -EINVAL;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* Disable PPMU */
210*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
211*4882a593Smuzhiyun 	if (ret < 0)
212*4882a593Smuzhiyun 		return ret;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
215*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
216*4882a593Smuzhiyun 	if (ret < 0)
217*4882a593Smuzhiyun 		return ret;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* Read cycle count */
220*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
221*4882a593Smuzhiyun 	if (ret < 0)
222*4882a593Smuzhiyun 		return ret;
223*4882a593Smuzhiyun 	edata->total_count = total_count;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	/* Read performance count */
226*4882a593Smuzhiyun 	switch (id) {
227*4882a593Smuzhiyun 	case PPMU_PMNCNT0:
228*4882a593Smuzhiyun 	case PPMU_PMNCNT1:
229*4882a593Smuzhiyun 	case PPMU_PMNCNT2:
230*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
231*4882a593Smuzhiyun 		if (ret < 0)
232*4882a593Smuzhiyun 			return ret;
233*4882a593Smuzhiyun 		edata->load_count = load_count;
234*4882a593Smuzhiyun 		break;
235*4882a593Smuzhiyun 	case PPMU_PMNCNT3:
236*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
237*4882a593Smuzhiyun 		if (ret < 0)
238*4882a593Smuzhiyun 			return ret;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
241*4882a593Smuzhiyun 		if (ret < 0)
242*4882a593Smuzhiyun 			return ret;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 		edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
245*4882a593Smuzhiyun 		break;
246*4882a593Smuzhiyun 	default:
247*4882a593Smuzhiyun 		return -EINVAL;
248*4882a593Smuzhiyun 	}
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	/* Disable specific counter */
251*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
252*4882a593Smuzhiyun 	if (ret < 0)
253*4882a593Smuzhiyun 		return ret;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
256*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
257*4882a593Smuzhiyun 	if (ret < 0)
258*4882a593Smuzhiyun 		return ret;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
261*4882a593Smuzhiyun 					edata->load_count, edata->total_count);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	return 0;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun static const struct devfreq_event_ops exynos_ppmu_ops = {
267*4882a593Smuzhiyun 	.disable = exynos_ppmu_disable,
268*4882a593Smuzhiyun 	.set_event = exynos_ppmu_set_event,
269*4882a593Smuzhiyun 	.get_event = exynos_ppmu_get_event,
270*4882a593Smuzhiyun };
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun /*
273*4882a593Smuzhiyun  * The devfreq-event ops structure for PPMU v2.0
274*4882a593Smuzhiyun  */
exynos_ppmu_v2_disable(struct devfreq_event_dev * edev)275*4882a593Smuzhiyun static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
278*4882a593Smuzhiyun 	int ret;
279*4882a593Smuzhiyun 	u32 pmnc, clear;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	/* Disable all counters */
282*4882a593Smuzhiyun 	clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
283*4882a593Smuzhiyun 		| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
284*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
285*4882a593Smuzhiyun 	if (ret < 0)
286*4882a593Smuzhiyun 		return ret;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
289*4882a593Smuzhiyun 	if (ret < 0)
290*4882a593Smuzhiyun 		return ret;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
293*4882a593Smuzhiyun 	if (ret < 0)
294*4882a593Smuzhiyun 		return ret;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
297*4882a593Smuzhiyun 	if (ret < 0)
298*4882a593Smuzhiyun 		return ret;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
301*4882a593Smuzhiyun 	if (ret < 0)
302*4882a593Smuzhiyun 		return ret;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
305*4882a593Smuzhiyun 	if (ret < 0)
306*4882a593Smuzhiyun 		return ret;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
309*4882a593Smuzhiyun 	if (ret < 0)
310*4882a593Smuzhiyun 		return ret;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
313*4882a593Smuzhiyun 	if (ret < 0)
314*4882a593Smuzhiyun 		return ret;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
317*4882a593Smuzhiyun 	if (ret < 0)
318*4882a593Smuzhiyun 		return ret;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
321*4882a593Smuzhiyun 	if (ret < 0)
322*4882a593Smuzhiyun 		return ret;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
325*4882a593Smuzhiyun 	if (ret < 0)
326*4882a593Smuzhiyun 		return ret;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
329*4882a593Smuzhiyun 	if (ret < 0)
330*4882a593Smuzhiyun 		return ret;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
333*4882a593Smuzhiyun 	if (ret < 0)
334*4882a593Smuzhiyun 		return ret;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
337*4882a593Smuzhiyun 	if (ret < 0)
338*4882a593Smuzhiyun 		return ret;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
341*4882a593Smuzhiyun 	if (ret < 0)
342*4882a593Smuzhiyun 		return ret;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
345*4882a593Smuzhiyun 	if (ret < 0)
346*4882a593Smuzhiyun 		return ret;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
349*4882a593Smuzhiyun 	if (ret < 0)
350*4882a593Smuzhiyun 		return ret;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
353*4882a593Smuzhiyun 	if (ret < 0)
354*4882a593Smuzhiyun 		return ret;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	/* Disable PPMU */
357*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
358*4882a593Smuzhiyun 	if (ret < 0)
359*4882a593Smuzhiyun 		return ret;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
362*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
363*4882a593Smuzhiyun 	if (ret < 0)
364*4882a593Smuzhiyun 		return ret;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	return 0;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
exynos_ppmu_v2_set_event(struct devfreq_event_dev * edev)369*4882a593Smuzhiyun static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
372*4882a593Smuzhiyun 	unsigned int pmnc, cntens;
373*4882a593Smuzhiyun 	int id = exynos_ppmu_find_ppmu_id(edev);
374*4882a593Smuzhiyun 	int ret;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	/* Enable all counters */
377*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
378*4882a593Smuzhiyun 	if (ret < 0)
379*4882a593Smuzhiyun 		return ret;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
382*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
383*4882a593Smuzhiyun 	if (ret < 0)
384*4882a593Smuzhiyun 		return ret;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	/* Set the event of proper data type monitoring */
387*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
388*4882a593Smuzhiyun 			   edev->desc->event_type);
389*4882a593Smuzhiyun 	if (ret < 0)
390*4882a593Smuzhiyun 		return ret;
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	/* Reset cycle counter/performance counter and enable PPMU */
393*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
394*4882a593Smuzhiyun 	if (ret < 0)
395*4882a593Smuzhiyun 		return ret;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
398*4882a593Smuzhiyun 			| PPMU_PMNC_COUNTER_RESET_MASK
399*4882a593Smuzhiyun 			| PPMU_PMNC_CC_RESET_MASK
400*4882a593Smuzhiyun 			| PPMU_PMNC_CC_DIVIDER_MASK
401*4882a593Smuzhiyun 			| PPMU_V2_PMNC_START_MODE_MASK);
402*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
403*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
404*4882a593Smuzhiyun 	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
405*4882a593Smuzhiyun 	pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
408*4882a593Smuzhiyun 	if (ret < 0)
409*4882a593Smuzhiyun 		return ret;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	return 0;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
exynos_ppmu_v2_get_event(struct devfreq_event_dev * edev,struct devfreq_event_data * edata)414*4882a593Smuzhiyun static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
415*4882a593Smuzhiyun 				    struct devfreq_event_data *edata)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun 	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
418*4882a593Smuzhiyun 	int id = exynos_ppmu_find_ppmu_id(edev);
419*4882a593Smuzhiyun 	int ret;
420*4882a593Smuzhiyun 	unsigned int pmnc, cntenc;
421*4882a593Smuzhiyun 	unsigned int pmcnt_high, pmcnt_low;
422*4882a593Smuzhiyun 	unsigned int total_count, count;
423*4882a593Smuzhiyun 	unsigned long load_count = 0;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	/* Disable PPMU */
426*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
427*4882a593Smuzhiyun 	if (ret < 0)
428*4882a593Smuzhiyun 		return ret;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
431*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
432*4882a593Smuzhiyun 	if (ret < 0)
433*4882a593Smuzhiyun 		return ret;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	/* Read cycle count and performance count */
436*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
437*4882a593Smuzhiyun 	if (ret < 0)
438*4882a593Smuzhiyun 		return ret;
439*4882a593Smuzhiyun 	edata->total_count = total_count;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	switch (id) {
442*4882a593Smuzhiyun 	case PPMU_PMNCNT0:
443*4882a593Smuzhiyun 	case PPMU_PMNCNT1:
444*4882a593Smuzhiyun 	case PPMU_PMNCNT2:
445*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
446*4882a593Smuzhiyun 		if (ret < 0)
447*4882a593Smuzhiyun 			return ret;
448*4882a593Smuzhiyun 		load_count = count;
449*4882a593Smuzhiyun 		break;
450*4882a593Smuzhiyun 	case PPMU_PMNCNT3:
451*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
452*4882a593Smuzhiyun 						&pmcnt_high);
453*4882a593Smuzhiyun 		if (ret < 0)
454*4882a593Smuzhiyun 			return ret;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
457*4882a593Smuzhiyun 		if (ret < 0)
458*4882a593Smuzhiyun 			return ret;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 		load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
461*4882a593Smuzhiyun 		break;
462*4882a593Smuzhiyun 	}
463*4882a593Smuzhiyun 	edata->load_count = load_count;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	/* Disable all counters */
466*4882a593Smuzhiyun 	ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
467*4882a593Smuzhiyun 	if (ret < 0)
468*4882a593Smuzhiyun 		return 0;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
471*4882a593Smuzhiyun 	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
472*4882a593Smuzhiyun 	if (ret < 0)
473*4882a593Smuzhiyun 		return ret;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
476*4882a593Smuzhiyun 					edata->load_count, edata->total_count);
477*4882a593Smuzhiyun 	return 0;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun static const struct devfreq_event_ops exynos_ppmu_v2_ops = {
481*4882a593Smuzhiyun 	.disable = exynos_ppmu_v2_disable,
482*4882a593Smuzhiyun 	.set_event = exynos_ppmu_v2_set_event,
483*4882a593Smuzhiyun 	.get_event = exynos_ppmu_v2_get_event,
484*4882a593Smuzhiyun };
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun static const struct of_device_id exynos_ppmu_id_match[] = {
487*4882a593Smuzhiyun 	{
488*4882a593Smuzhiyun 		.compatible = "samsung,exynos-ppmu",
489*4882a593Smuzhiyun 		.data = (void *)EXYNOS_TYPE_PPMU,
490*4882a593Smuzhiyun 	}, {
491*4882a593Smuzhiyun 		.compatible = "samsung,exynos-ppmu-v2",
492*4882a593Smuzhiyun 		.data = (void *)EXYNOS_TYPE_PPMU_V2,
493*4882a593Smuzhiyun 	},
494*4882a593Smuzhiyun 	{ /* sentinel */ },
495*4882a593Smuzhiyun };
496*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);
497*4882a593Smuzhiyun 
of_get_devfreq_events(struct device_node * np,struct exynos_ppmu * info)498*4882a593Smuzhiyun static int of_get_devfreq_events(struct device_node *np,
499*4882a593Smuzhiyun 				 struct exynos_ppmu *info)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun 	struct devfreq_event_desc *desc;
502*4882a593Smuzhiyun 	struct device *dev = info->dev;
503*4882a593Smuzhiyun 	struct device_node *events_np, *node;
504*4882a593Smuzhiyun 	int i, j, count;
505*4882a593Smuzhiyun 	const struct of_device_id *of_id;
506*4882a593Smuzhiyun 	int ret;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	events_np = of_get_child_by_name(np, "events");
509*4882a593Smuzhiyun 	if (!events_np) {
510*4882a593Smuzhiyun 		dev_err(dev,
511*4882a593Smuzhiyun 			"failed to get child node of devfreq-event devices\n");
512*4882a593Smuzhiyun 		return -EINVAL;
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	count = of_get_child_count(events_np);
516*4882a593Smuzhiyun 	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
517*4882a593Smuzhiyun 	if (!desc) {
518*4882a593Smuzhiyun 		of_node_put(events_np);
519*4882a593Smuzhiyun 		return -ENOMEM;
520*4882a593Smuzhiyun 	}
521*4882a593Smuzhiyun 	info->num_events = count;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	of_id = of_match_device(exynos_ppmu_id_match, dev);
524*4882a593Smuzhiyun 	if (of_id)
525*4882a593Smuzhiyun 		info->ppmu_type = (enum exynos_ppmu_type)of_id->data;
526*4882a593Smuzhiyun 	else {
527*4882a593Smuzhiyun 		of_node_put(events_np);
528*4882a593Smuzhiyun 		return -EINVAL;
529*4882a593Smuzhiyun 	}
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	j = 0;
532*4882a593Smuzhiyun 	for_each_child_of_node(events_np, node) {
533*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
534*4882a593Smuzhiyun 			if (!ppmu_events[i].name)
535*4882a593Smuzhiyun 				continue;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 			if (of_node_name_eq(node, ppmu_events[i].name))
538*4882a593Smuzhiyun 				break;
539*4882a593Smuzhiyun 		}
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 		if (i == ARRAY_SIZE(ppmu_events)) {
542*4882a593Smuzhiyun 			dev_warn(dev,
543*4882a593Smuzhiyun 				"don't know how to configure events : %pOFn\n",
544*4882a593Smuzhiyun 				node);
545*4882a593Smuzhiyun 			continue;
546*4882a593Smuzhiyun 		}
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 		switch (info->ppmu_type) {
549*4882a593Smuzhiyun 		case EXYNOS_TYPE_PPMU:
550*4882a593Smuzhiyun 			desc[j].ops = &exynos_ppmu_ops;
551*4882a593Smuzhiyun 			break;
552*4882a593Smuzhiyun 		case EXYNOS_TYPE_PPMU_V2:
553*4882a593Smuzhiyun 			desc[j].ops = &exynos_ppmu_v2_ops;
554*4882a593Smuzhiyun 			break;
555*4882a593Smuzhiyun 		}
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 		desc[j].driver_data = info;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 		of_property_read_string(node, "event-name", &desc[j].name);
560*4882a593Smuzhiyun 		ret = of_property_read_u32(node, "event-data-type",
561*4882a593Smuzhiyun 					   &desc[j].event_type);
562*4882a593Smuzhiyun 		if (ret) {
563*4882a593Smuzhiyun 			/* Set the event of proper data type counting.
564*4882a593Smuzhiyun 			 * Check if the data type has been defined in DT,
565*4882a593Smuzhiyun 			 * use default if not.
566*4882a593Smuzhiyun 			 */
567*4882a593Smuzhiyun 			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {
568*4882a593Smuzhiyun 				int id;
569*4882a593Smuzhiyun 				/* Not all registers take the same value for
570*4882a593Smuzhiyun 				 * read+write data count.
571*4882a593Smuzhiyun 				 */
572*4882a593Smuzhiyun 				id = __exynos_ppmu_find_ppmu_id(desc[j].name);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 				switch (id) {
575*4882a593Smuzhiyun 				case PPMU_PMNCNT0:
576*4882a593Smuzhiyun 				case PPMU_PMNCNT1:
577*4882a593Smuzhiyun 				case PPMU_PMNCNT2:
578*4882a593Smuzhiyun 					desc[j].event_type = PPMU_V2_RO_DATA_CNT
579*4882a593Smuzhiyun 						| PPMU_V2_WO_DATA_CNT;
580*4882a593Smuzhiyun 					break;
581*4882a593Smuzhiyun 				case PPMU_PMNCNT3:
582*4882a593Smuzhiyun 					desc[j].event_type =
583*4882a593Smuzhiyun 						PPMU_V2_EVT3_RW_DATA_CNT;
584*4882a593Smuzhiyun 					break;
585*4882a593Smuzhiyun 				}
586*4882a593Smuzhiyun 			} else {
587*4882a593Smuzhiyun 				desc[j].event_type = PPMU_RO_DATA_CNT |
588*4882a593Smuzhiyun 					PPMU_WO_DATA_CNT;
589*4882a593Smuzhiyun 			}
590*4882a593Smuzhiyun 		}
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 		j++;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 	info->desc = desc;
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	of_node_put(events_np);
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	return 0;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun static struct regmap_config exynos_ppmu_regmap_config = {
602*4882a593Smuzhiyun 	.reg_bits = 32,
603*4882a593Smuzhiyun 	.val_bits = 32,
604*4882a593Smuzhiyun 	.reg_stride = 4,
605*4882a593Smuzhiyun };
606*4882a593Smuzhiyun 
exynos_ppmu_parse_dt(struct platform_device * pdev,struct exynos_ppmu * info)607*4882a593Smuzhiyun static int exynos_ppmu_parse_dt(struct platform_device *pdev,
608*4882a593Smuzhiyun 				struct exynos_ppmu *info)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun 	struct device *dev = info->dev;
611*4882a593Smuzhiyun 	struct device_node *np = dev->of_node;
612*4882a593Smuzhiyun 	struct resource *res;
613*4882a593Smuzhiyun 	void __iomem *base;
614*4882a593Smuzhiyun 	int ret = 0;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	if (!np) {
617*4882a593Smuzhiyun 		dev_err(dev, "failed to find devicetree node\n");
618*4882a593Smuzhiyun 		return -EINVAL;
619*4882a593Smuzhiyun 	}
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	/* Maps the memory mapped IO to control PPMU register */
622*4882a593Smuzhiyun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
623*4882a593Smuzhiyun 	base = devm_ioremap_resource(dev, res);
624*4882a593Smuzhiyun 	if (IS_ERR(base))
625*4882a593Smuzhiyun 		return PTR_ERR(base);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
628*4882a593Smuzhiyun 	info->regmap = devm_regmap_init_mmio(dev, base,
629*4882a593Smuzhiyun 					&exynos_ppmu_regmap_config);
630*4882a593Smuzhiyun 	if (IS_ERR(info->regmap)) {
631*4882a593Smuzhiyun 		dev_err(dev, "failed to initialize regmap\n");
632*4882a593Smuzhiyun 		return PTR_ERR(info->regmap);
633*4882a593Smuzhiyun 	}
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	info->ppmu.clk = devm_clk_get(dev, "ppmu");
636*4882a593Smuzhiyun 	if (IS_ERR(info->ppmu.clk)) {
637*4882a593Smuzhiyun 		info->ppmu.clk = NULL;
638*4882a593Smuzhiyun 		dev_warn(dev, "cannot get PPMU clock\n");
639*4882a593Smuzhiyun 	}
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	ret = of_get_devfreq_events(np, info);
642*4882a593Smuzhiyun 	if (ret < 0) {
643*4882a593Smuzhiyun 		dev_err(dev, "failed to parse exynos ppmu dt node\n");
644*4882a593Smuzhiyun 		return ret;
645*4882a593Smuzhiyun 	}
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	return 0;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun 
exynos_ppmu_probe(struct platform_device * pdev)650*4882a593Smuzhiyun static int exynos_ppmu_probe(struct platform_device *pdev)
651*4882a593Smuzhiyun {
652*4882a593Smuzhiyun 	struct exynos_ppmu *info;
653*4882a593Smuzhiyun 	struct devfreq_event_dev **edev;
654*4882a593Smuzhiyun 	struct devfreq_event_desc *desc;
655*4882a593Smuzhiyun 	int i, ret = 0, size;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
658*4882a593Smuzhiyun 	if (!info)
659*4882a593Smuzhiyun 		return -ENOMEM;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	info->dev = &pdev->dev;
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	/* Parse dt data to get resource */
664*4882a593Smuzhiyun 	ret = exynos_ppmu_parse_dt(pdev, info);
665*4882a593Smuzhiyun 	if (ret < 0) {
666*4882a593Smuzhiyun 		dev_err(&pdev->dev,
667*4882a593Smuzhiyun 			"failed to parse devicetree for resource\n");
668*4882a593Smuzhiyun 		return ret;
669*4882a593Smuzhiyun 	}
670*4882a593Smuzhiyun 	desc = info->desc;
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	size = sizeof(struct devfreq_event_dev *) * info->num_events;
673*4882a593Smuzhiyun 	info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
674*4882a593Smuzhiyun 	if (!info->edev)
675*4882a593Smuzhiyun 		return -ENOMEM;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	edev = info->edev;
678*4882a593Smuzhiyun 	platform_set_drvdata(pdev, info);
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	for (i = 0; i < info->num_events; i++) {
681*4882a593Smuzhiyun 		edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
682*4882a593Smuzhiyun 		if (IS_ERR(edev[i])) {
683*4882a593Smuzhiyun 			dev_err(&pdev->dev,
684*4882a593Smuzhiyun 				"failed to add devfreq-event device\n");
685*4882a593Smuzhiyun 			return PTR_ERR(edev[i]);
686*4882a593Smuzhiyun 		}
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 		pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
689*4882a593Smuzhiyun 			dev_name(&pdev->dev), desc[i].name);
690*4882a593Smuzhiyun 	}
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	ret = clk_prepare_enable(info->ppmu.clk);
693*4882a593Smuzhiyun 	if (ret) {
694*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
695*4882a593Smuzhiyun 		return ret;
696*4882a593Smuzhiyun 	}
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	return 0;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun 
exynos_ppmu_remove(struct platform_device * pdev)701*4882a593Smuzhiyun static int exynos_ppmu_remove(struct platform_device *pdev)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun 	struct exynos_ppmu *info = platform_get_drvdata(pdev);
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	clk_disable_unprepare(info->ppmu.clk);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	return 0;
708*4882a593Smuzhiyun }
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun static struct platform_driver exynos_ppmu_driver = {
711*4882a593Smuzhiyun 	.probe	= exynos_ppmu_probe,
712*4882a593Smuzhiyun 	.remove	= exynos_ppmu_remove,
713*4882a593Smuzhiyun 	.driver = {
714*4882a593Smuzhiyun 		.name	= "exynos-ppmu",
715*4882a593Smuzhiyun 		.of_match_table = exynos_ppmu_id_match,
716*4882a593Smuzhiyun 	},
717*4882a593Smuzhiyun };
718*4882a593Smuzhiyun module_platform_driver(exynos_ppmu_driver);
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
721*4882a593Smuzhiyun MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
722*4882a593Smuzhiyun MODULE_LICENSE("GPL");
723