162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Author : Chanwoo Choi <cw00.choi@samsung.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of_address.h>
1662306a36Sopenharmony_ci#include <linux/of_device.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/suspend.h>
2062306a36Sopenharmony_ci#include <linux/devfreq-event.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "exynos-ppmu.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cienum exynos_ppmu_type {
2562306a36Sopenharmony_ci	EXYNOS_TYPE_PPMU,
2662306a36Sopenharmony_ci	EXYNOS_TYPE_PPMU_V2,
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct exynos_ppmu_data {
3062306a36Sopenharmony_ci	struct clk *clk;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct exynos_ppmu {
3462306a36Sopenharmony_ci	struct devfreq_event_dev **edev;
3562306a36Sopenharmony_ci	struct devfreq_event_desc *desc;
3662306a36Sopenharmony_ci	unsigned int num_events;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	struct device *dev;
3962306a36Sopenharmony_ci	struct regmap *regmap;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	struct exynos_ppmu_data ppmu;
4262306a36Sopenharmony_ci	enum exynos_ppmu_type ppmu_type;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define PPMU_EVENT(name)			\
4662306a36Sopenharmony_ci	{ "ppmu-event0-"#name, PPMU_PMNCNT0 },	\
4762306a36Sopenharmony_ci	{ "ppmu-event1-"#name, PPMU_PMNCNT1 },	\
4862306a36Sopenharmony_ci	{ "ppmu-event2-"#name, PPMU_PMNCNT2 },	\
4962306a36Sopenharmony_ci	{ "ppmu-event3-"#name, PPMU_PMNCNT3 }
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct __exynos_ppmu_events {
5262306a36Sopenharmony_ci	char *name;
5362306a36Sopenharmony_ci	int id;
5462306a36Sopenharmony_ci} ppmu_events[] = {
5562306a36Sopenharmony_ci	/* For Exynos3250, Exynos4 and Exynos5260 */
5662306a36Sopenharmony_ci	PPMU_EVENT(g3d),
5762306a36Sopenharmony_ci	PPMU_EVENT(fsys),
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* For Exynos4 SoCs and Exynos3250 */
6062306a36Sopenharmony_ci	PPMU_EVENT(dmc0),
6162306a36Sopenharmony_ci	PPMU_EVENT(dmc1),
6262306a36Sopenharmony_ci	PPMU_EVENT(cpu),
6362306a36Sopenharmony_ci	PPMU_EVENT(rightbus),
6462306a36Sopenharmony_ci	PPMU_EVENT(leftbus),
6562306a36Sopenharmony_ci	PPMU_EVENT(lcd0),
6662306a36Sopenharmony_ci	PPMU_EVENT(camif),
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Only for Exynos3250 and Exynos5260 */
6962306a36Sopenharmony_ci	PPMU_EVENT(mfc),
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* Only for Exynos4 SoCs */
7262306a36Sopenharmony_ci	PPMU_EVENT(mfc-left),
7362306a36Sopenharmony_ci	PPMU_EVENT(mfc-right),
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Only for Exynos5260 SoCs */
7662306a36Sopenharmony_ci	PPMU_EVENT(drex0-s0),
7762306a36Sopenharmony_ci	PPMU_EVENT(drex0-s1),
7862306a36Sopenharmony_ci	PPMU_EVENT(drex1-s0),
7962306a36Sopenharmony_ci	PPMU_EVENT(drex1-s1),
8062306a36Sopenharmony_ci	PPMU_EVENT(eagle),
8162306a36Sopenharmony_ci	PPMU_EVENT(kfc),
8262306a36Sopenharmony_ci	PPMU_EVENT(isp),
8362306a36Sopenharmony_ci	PPMU_EVENT(fimc),
8462306a36Sopenharmony_ci	PPMU_EVENT(gscl),
8562306a36Sopenharmony_ci	PPMU_EVENT(mscl),
8662306a36Sopenharmony_ci	PPMU_EVENT(fimd0x),
8762306a36Sopenharmony_ci	PPMU_EVENT(fimd1x),
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Only for Exynos5433 SoCs */
9062306a36Sopenharmony_ci	PPMU_EVENT(d0-cpu),
9162306a36Sopenharmony_ci	PPMU_EVENT(d0-general),
9262306a36Sopenharmony_ci	PPMU_EVENT(d0-rt),
9362306a36Sopenharmony_ci	PPMU_EVENT(d1-cpu),
9462306a36Sopenharmony_ci	PPMU_EVENT(d1-general),
9562306a36Sopenharmony_ci	PPMU_EVENT(d1-rt),
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* For Exynos5422 SoC, deprecated (backwards compatible) */
9862306a36Sopenharmony_ci	PPMU_EVENT(dmc0_0),
9962306a36Sopenharmony_ci	PPMU_EVENT(dmc0_1),
10062306a36Sopenharmony_ci	PPMU_EVENT(dmc1_0),
10162306a36Sopenharmony_ci	PPMU_EVENT(dmc1_1),
10262306a36Sopenharmony_ci	/* For Exynos5422 SoC */
10362306a36Sopenharmony_ci	PPMU_EVENT(dmc0-0),
10462306a36Sopenharmony_ci	PPMU_EVENT(dmc0-1),
10562306a36Sopenharmony_ci	PPMU_EVENT(dmc1-0),
10662306a36Sopenharmony_ci	PPMU_EVENT(dmc1-1),
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int __exynos_ppmu_find_ppmu_id(const char *edev_name)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int i;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
11462306a36Sopenharmony_ci		if (!strcmp(edev_name, ppmu_events[i].name))
11562306a36Sopenharmony_ci			return ppmu_events[i].id;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return -EINVAL;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	return __exynos_ppmu_find_ppmu_id(edev->desc->name);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/*
12662306a36Sopenharmony_ci * The devfreq-event ops structure for PPMU v1.1
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic int exynos_ppmu_disable(struct devfreq_event_dev *edev)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
13162306a36Sopenharmony_ci	int ret;
13262306a36Sopenharmony_ci	u32 pmnc;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Disable all counters */
13562306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENC,
13662306a36Sopenharmony_ci				PPMU_CCNT_MASK |
13762306a36Sopenharmony_ci				PPMU_PMCNT0_MASK |
13862306a36Sopenharmony_ci				PPMU_PMCNT1_MASK |
13962306a36Sopenharmony_ci				PPMU_PMCNT2_MASK |
14062306a36Sopenharmony_ci				PPMU_PMCNT3_MASK);
14162306a36Sopenharmony_ci	if (ret < 0)
14262306a36Sopenharmony_ci		return ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Disable PPMU */
14562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
14662306a36Sopenharmony_ci	if (ret < 0)
14762306a36Sopenharmony_ci		return ret;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
15062306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
15162306a36Sopenharmony_ci	if (ret < 0)
15262306a36Sopenharmony_ci		return ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
16062306a36Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
16162306a36Sopenharmony_ci	int ret;
16262306a36Sopenharmony_ci	u32 pmnc, cntens;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (id < 0)
16562306a36Sopenharmony_ci		return id;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Enable specific counter */
16862306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
16962306a36Sopenharmony_ci	if (ret < 0)
17062306a36Sopenharmony_ci		return ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
17362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
17462306a36Sopenharmony_ci	if (ret < 0)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Set the event of proper data type monitoring */
17862306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
17962306a36Sopenharmony_ci			   edev->desc->event_type);
18062306a36Sopenharmony_ci	if (ret < 0)
18162306a36Sopenharmony_ci		return ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Reset cycle counter/performance counter and enable PPMU */
18462306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
18562306a36Sopenharmony_ci	if (ret < 0)
18662306a36Sopenharmony_ci		return ret;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
18962306a36Sopenharmony_ci			| PPMU_PMNC_COUNTER_RESET_MASK
19062306a36Sopenharmony_ci			| PPMU_PMNC_CC_RESET_MASK);
19162306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
19262306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
19362306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
19462306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
19562306a36Sopenharmony_ci	if (ret < 0)
19662306a36Sopenharmony_ci		return ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
20262306a36Sopenharmony_ci				struct devfreq_event_data *edata)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
20562306a36Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
20662306a36Sopenharmony_ci	unsigned int total_count, load_count;
20762306a36Sopenharmony_ci	unsigned int pmcnt3_high, pmcnt3_low;
20862306a36Sopenharmony_ci	unsigned int pmnc, cntenc;
20962306a36Sopenharmony_ci	int ret;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (id < 0)
21262306a36Sopenharmony_ci		return -EINVAL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Disable PPMU */
21562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
21662306a36Sopenharmony_ci	if (ret < 0)
21762306a36Sopenharmony_ci		return ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
22062306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
22162306a36Sopenharmony_ci	if (ret < 0)
22262306a36Sopenharmony_ci		return ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Read cycle count */
22562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
22662306a36Sopenharmony_ci	if (ret < 0)
22762306a36Sopenharmony_ci		return ret;
22862306a36Sopenharmony_ci	edata->total_count = total_count;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* Read performance count */
23162306a36Sopenharmony_ci	switch (id) {
23262306a36Sopenharmony_ci	case PPMU_PMNCNT0:
23362306a36Sopenharmony_ci	case PPMU_PMNCNT1:
23462306a36Sopenharmony_ci	case PPMU_PMNCNT2:
23562306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
23662306a36Sopenharmony_ci		if (ret < 0)
23762306a36Sopenharmony_ci			return ret;
23862306a36Sopenharmony_ci		edata->load_count = load_count;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case PPMU_PMNCNT3:
24162306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
24262306a36Sopenharmony_ci		if (ret < 0)
24362306a36Sopenharmony_ci			return ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
24662306a36Sopenharmony_ci		if (ret < 0)
24762306a36Sopenharmony_ci			return ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci	default:
25262306a36Sopenharmony_ci		return -EINVAL;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Disable specific counter */
25662306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
25762306a36Sopenharmony_ci	if (ret < 0)
25862306a36Sopenharmony_ci		return ret;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
26162306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
26262306a36Sopenharmony_ci	if (ret < 0)
26362306a36Sopenharmony_ci		return ret;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
26662306a36Sopenharmony_ci					edata->load_count, edata->total_count);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic const struct devfreq_event_ops exynos_ppmu_ops = {
27262306a36Sopenharmony_ci	.disable = exynos_ppmu_disable,
27362306a36Sopenharmony_ci	.set_event = exynos_ppmu_set_event,
27462306a36Sopenharmony_ci	.get_event = exynos_ppmu_get_event,
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * The devfreq-event ops structure for PPMU v2.0
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
28362306a36Sopenharmony_ci	int ret;
28462306a36Sopenharmony_ci	u32 pmnc, clear;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Disable all counters */
28762306a36Sopenharmony_ci	clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
28862306a36Sopenharmony_ci		| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
28962306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
29062306a36Sopenharmony_ci	if (ret < 0)
29162306a36Sopenharmony_ci		return ret;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
29462306a36Sopenharmony_ci	if (ret < 0)
29562306a36Sopenharmony_ci		return ret;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
29862306a36Sopenharmony_ci	if (ret < 0)
29962306a36Sopenharmony_ci		return ret;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
30262306a36Sopenharmony_ci	if (ret < 0)
30362306a36Sopenharmony_ci		return ret;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
30662306a36Sopenharmony_ci	if (ret < 0)
30762306a36Sopenharmony_ci		return ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
31062306a36Sopenharmony_ci	if (ret < 0)
31162306a36Sopenharmony_ci		return ret;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
31462306a36Sopenharmony_ci	if (ret < 0)
31562306a36Sopenharmony_ci		return ret;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
31862306a36Sopenharmony_ci	if (ret < 0)
31962306a36Sopenharmony_ci		return ret;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
32262306a36Sopenharmony_ci	if (ret < 0)
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
32662306a36Sopenharmony_ci	if (ret < 0)
32762306a36Sopenharmony_ci		return ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
33062306a36Sopenharmony_ci	if (ret < 0)
33162306a36Sopenharmony_ci		return ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
33462306a36Sopenharmony_ci	if (ret < 0)
33562306a36Sopenharmony_ci		return ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
33862306a36Sopenharmony_ci	if (ret < 0)
33962306a36Sopenharmony_ci		return ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
34262306a36Sopenharmony_ci	if (ret < 0)
34362306a36Sopenharmony_ci		return ret;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
34662306a36Sopenharmony_ci	if (ret < 0)
34762306a36Sopenharmony_ci		return ret;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
35062306a36Sopenharmony_ci	if (ret < 0)
35162306a36Sopenharmony_ci		return ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
35462306a36Sopenharmony_ci	if (ret < 0)
35562306a36Sopenharmony_ci		return ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
35862306a36Sopenharmony_ci	if (ret < 0)
35962306a36Sopenharmony_ci		return ret;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* Disable PPMU */
36262306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
36362306a36Sopenharmony_ci	if (ret < 0)
36462306a36Sopenharmony_ci		return ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
36762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
36862306a36Sopenharmony_ci	if (ret < 0)
36962306a36Sopenharmony_ci		return ret;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
37762306a36Sopenharmony_ci	unsigned int pmnc, cntens;
37862306a36Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
37962306a36Sopenharmony_ci	int ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* Enable all counters */
38262306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
38362306a36Sopenharmony_ci	if (ret < 0)
38462306a36Sopenharmony_ci		return ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
38762306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
38862306a36Sopenharmony_ci	if (ret < 0)
38962306a36Sopenharmony_ci		return ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Set the event of proper data type monitoring */
39262306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
39362306a36Sopenharmony_ci			   edev->desc->event_type);
39462306a36Sopenharmony_ci	if (ret < 0)
39562306a36Sopenharmony_ci		return ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* Reset cycle counter/performance counter and enable PPMU */
39862306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
39962306a36Sopenharmony_ci	if (ret < 0)
40062306a36Sopenharmony_ci		return ret;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
40362306a36Sopenharmony_ci			| PPMU_PMNC_COUNTER_RESET_MASK
40462306a36Sopenharmony_ci			| PPMU_PMNC_CC_RESET_MASK
40562306a36Sopenharmony_ci			| PPMU_PMNC_CC_DIVIDER_MASK
40662306a36Sopenharmony_ci			| PPMU_V2_PMNC_START_MODE_MASK);
40762306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
40862306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
40962306a36Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
41062306a36Sopenharmony_ci	pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
41362306a36Sopenharmony_ci	if (ret < 0)
41462306a36Sopenharmony_ci		return ret;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return 0;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
42062306a36Sopenharmony_ci				    struct devfreq_event_data *edata)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
42362306a36Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
42462306a36Sopenharmony_ci	int ret;
42562306a36Sopenharmony_ci	unsigned int pmnc, cntenc;
42662306a36Sopenharmony_ci	unsigned int pmcnt_high, pmcnt_low;
42762306a36Sopenharmony_ci	unsigned int total_count, count;
42862306a36Sopenharmony_ci	unsigned long load_count = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Disable PPMU */
43162306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
43262306a36Sopenharmony_ci	if (ret < 0)
43362306a36Sopenharmony_ci		return ret;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
43662306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
43762306a36Sopenharmony_ci	if (ret < 0)
43862306a36Sopenharmony_ci		return ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* Read cycle count and performance count */
44162306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
44262306a36Sopenharmony_ci	if (ret < 0)
44362306a36Sopenharmony_ci		return ret;
44462306a36Sopenharmony_ci	edata->total_count = total_count;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	switch (id) {
44762306a36Sopenharmony_ci	case PPMU_PMNCNT0:
44862306a36Sopenharmony_ci	case PPMU_PMNCNT1:
44962306a36Sopenharmony_ci	case PPMU_PMNCNT2:
45062306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
45162306a36Sopenharmony_ci		if (ret < 0)
45262306a36Sopenharmony_ci			return ret;
45362306a36Sopenharmony_ci		load_count = count;
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case PPMU_PMNCNT3:
45662306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
45762306a36Sopenharmony_ci						&pmcnt_high);
45862306a36Sopenharmony_ci		if (ret < 0)
45962306a36Sopenharmony_ci			return ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
46262306a36Sopenharmony_ci		if (ret < 0)
46362306a36Sopenharmony_ci			return ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci	edata->load_count = load_count;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Disable all counters */
47162306a36Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
47262306a36Sopenharmony_ci	if (ret < 0)
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
47662306a36Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
47762306a36Sopenharmony_ci	if (ret < 0)
47862306a36Sopenharmony_ci		return ret;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
48162306a36Sopenharmony_ci					edata->load_count, edata->total_count);
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic const struct devfreq_event_ops exynos_ppmu_v2_ops = {
48662306a36Sopenharmony_ci	.disable = exynos_ppmu_v2_disable,
48762306a36Sopenharmony_ci	.set_event = exynos_ppmu_v2_set_event,
48862306a36Sopenharmony_ci	.get_event = exynos_ppmu_v2_get_event,
48962306a36Sopenharmony_ci};
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic const struct of_device_id exynos_ppmu_id_match[] = {
49262306a36Sopenharmony_ci	{
49362306a36Sopenharmony_ci		.compatible = "samsung,exynos-ppmu",
49462306a36Sopenharmony_ci		.data = (void *)EXYNOS_TYPE_PPMU,
49562306a36Sopenharmony_ci	}, {
49662306a36Sopenharmony_ci		.compatible = "samsung,exynos-ppmu-v2",
49762306a36Sopenharmony_ci		.data = (void *)EXYNOS_TYPE_PPMU_V2,
49862306a36Sopenharmony_ci	},
49962306a36Sopenharmony_ci	{ /* sentinel */ },
50062306a36Sopenharmony_ci};
50162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int of_get_devfreq_events(struct device_node *np,
50462306a36Sopenharmony_ci				 struct exynos_ppmu *info)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct devfreq_event_desc *desc;
50762306a36Sopenharmony_ci	struct device *dev = info->dev;
50862306a36Sopenharmony_ci	struct device_node *events_np, *node;
50962306a36Sopenharmony_ci	int i, j, count;
51062306a36Sopenharmony_ci	const struct of_device_id *of_id;
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	events_np = of_get_child_by_name(np, "events");
51462306a36Sopenharmony_ci	if (!events_np) {
51562306a36Sopenharmony_ci		dev_err(dev,
51662306a36Sopenharmony_ci			"failed to get child node of devfreq-event devices\n");
51762306a36Sopenharmony_ci		return -EINVAL;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	count = of_get_child_count(events_np);
52162306a36Sopenharmony_ci	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
52262306a36Sopenharmony_ci	if (!desc) {
52362306a36Sopenharmony_ci		of_node_put(events_np);
52462306a36Sopenharmony_ci		return -ENOMEM;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	info->num_events = count;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	of_id = of_match_device(exynos_ppmu_id_match, dev);
52962306a36Sopenharmony_ci	if (of_id)
53062306a36Sopenharmony_ci		info->ppmu_type = (enum exynos_ppmu_type)of_id->data;
53162306a36Sopenharmony_ci	else {
53262306a36Sopenharmony_ci		of_node_put(events_np);
53362306a36Sopenharmony_ci		return -EINVAL;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	j = 0;
53762306a36Sopenharmony_ci	for_each_child_of_node(events_np, node) {
53862306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
53962306a36Sopenharmony_ci			if (!ppmu_events[i].name)
54062306a36Sopenharmony_ci				continue;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci			if (of_node_name_eq(node, ppmu_events[i].name))
54362306a36Sopenharmony_ci				break;
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (i == ARRAY_SIZE(ppmu_events)) {
54762306a36Sopenharmony_ci			dev_warn(dev,
54862306a36Sopenharmony_ci				"don't know how to configure events : %pOFn\n",
54962306a36Sopenharmony_ci				node);
55062306a36Sopenharmony_ci			continue;
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		switch (info->ppmu_type) {
55462306a36Sopenharmony_ci		case EXYNOS_TYPE_PPMU:
55562306a36Sopenharmony_ci			desc[j].ops = &exynos_ppmu_ops;
55662306a36Sopenharmony_ci			break;
55762306a36Sopenharmony_ci		case EXYNOS_TYPE_PPMU_V2:
55862306a36Sopenharmony_ci			desc[j].ops = &exynos_ppmu_v2_ops;
55962306a36Sopenharmony_ci			break;
56062306a36Sopenharmony_ci		}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		desc[j].driver_data = info;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		of_property_read_string(node, "event-name", &desc[j].name);
56562306a36Sopenharmony_ci		ret = of_property_read_u32(node, "event-data-type",
56662306a36Sopenharmony_ci					   &desc[j].event_type);
56762306a36Sopenharmony_ci		if (ret) {
56862306a36Sopenharmony_ci			/* Set the event of proper data type counting.
56962306a36Sopenharmony_ci			 * Check if the data type has been defined in DT,
57062306a36Sopenharmony_ci			 * use default if not.
57162306a36Sopenharmony_ci			 */
57262306a36Sopenharmony_ci			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {
57362306a36Sopenharmony_ci				/* Not all registers take the same value for
57462306a36Sopenharmony_ci				 * read+write data count.
57562306a36Sopenharmony_ci				 */
57662306a36Sopenharmony_ci				switch (ppmu_events[i].id) {
57762306a36Sopenharmony_ci				case PPMU_PMNCNT0:
57862306a36Sopenharmony_ci				case PPMU_PMNCNT1:
57962306a36Sopenharmony_ci				case PPMU_PMNCNT2:
58062306a36Sopenharmony_ci					desc[j].event_type = PPMU_V2_RO_DATA_CNT
58162306a36Sopenharmony_ci						| PPMU_V2_WO_DATA_CNT;
58262306a36Sopenharmony_ci					break;
58362306a36Sopenharmony_ci				case PPMU_PMNCNT3:
58462306a36Sopenharmony_ci					desc[j].event_type =
58562306a36Sopenharmony_ci						PPMU_V2_EVT3_RW_DATA_CNT;
58662306a36Sopenharmony_ci					break;
58762306a36Sopenharmony_ci				}
58862306a36Sopenharmony_ci			} else {
58962306a36Sopenharmony_ci				desc[j].event_type = PPMU_RO_DATA_CNT |
59062306a36Sopenharmony_ci					PPMU_WO_DATA_CNT;
59162306a36Sopenharmony_ci			}
59262306a36Sopenharmony_ci		}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		j++;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	info->desc = desc;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	of_node_put(events_np);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return 0;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic struct regmap_config exynos_ppmu_regmap_config = {
60462306a36Sopenharmony_ci	.reg_bits = 32,
60562306a36Sopenharmony_ci	.val_bits = 32,
60662306a36Sopenharmony_ci	.reg_stride = 4,
60762306a36Sopenharmony_ci};
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int exynos_ppmu_parse_dt(struct platform_device *pdev,
61062306a36Sopenharmony_ci				struct exynos_ppmu *info)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct device *dev = info->dev;
61362306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
61462306a36Sopenharmony_ci	struct resource *res;
61562306a36Sopenharmony_ci	void __iomem *base;
61662306a36Sopenharmony_ci	int ret = 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (!np) {
61962306a36Sopenharmony_ci		dev_err(dev, "failed to find devicetree node\n");
62062306a36Sopenharmony_ci		return -EINVAL;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* Maps the memory mapped IO to control PPMU register */
62462306a36Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
62562306a36Sopenharmony_ci	if (IS_ERR(base))
62662306a36Sopenharmony_ci		return PTR_ERR(base);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
62962306a36Sopenharmony_ci	info->regmap = devm_regmap_init_mmio(dev, base,
63062306a36Sopenharmony_ci					&exynos_ppmu_regmap_config);
63162306a36Sopenharmony_ci	if (IS_ERR(info->regmap)) {
63262306a36Sopenharmony_ci		dev_err(dev, "failed to initialize regmap\n");
63362306a36Sopenharmony_ci		return PTR_ERR(info->regmap);
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	info->ppmu.clk = devm_clk_get(dev, "ppmu");
63762306a36Sopenharmony_ci	if (IS_ERR(info->ppmu.clk)) {
63862306a36Sopenharmony_ci		info->ppmu.clk = NULL;
63962306a36Sopenharmony_ci		dev_warn(dev, "cannot get PPMU clock\n");
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	ret = of_get_devfreq_events(np, info);
64362306a36Sopenharmony_ci	if (ret < 0) {
64462306a36Sopenharmony_ci		dev_err(dev, "failed to parse exynos ppmu dt node\n");
64562306a36Sopenharmony_ci		return ret;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	return 0;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic int exynos_ppmu_probe(struct platform_device *pdev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct exynos_ppmu *info;
65462306a36Sopenharmony_ci	struct devfreq_event_dev **edev;
65562306a36Sopenharmony_ci	struct devfreq_event_desc *desc;
65662306a36Sopenharmony_ci	int i, ret = 0, size;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
65962306a36Sopenharmony_ci	if (!info)
66062306a36Sopenharmony_ci		return -ENOMEM;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	info->dev = &pdev->dev;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Parse dt data to get resource */
66562306a36Sopenharmony_ci	ret = exynos_ppmu_parse_dt(pdev, info);
66662306a36Sopenharmony_ci	if (ret < 0) {
66762306a36Sopenharmony_ci		dev_err(&pdev->dev,
66862306a36Sopenharmony_ci			"failed to parse devicetree for resource\n");
66962306a36Sopenharmony_ci		return ret;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	desc = info->desc;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	size = sizeof(struct devfreq_event_dev *) * info->num_events;
67462306a36Sopenharmony_ci	info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
67562306a36Sopenharmony_ci	if (!info->edev)
67662306a36Sopenharmony_ci		return -ENOMEM;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	edev = info->edev;
67962306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for (i = 0; i < info->num_events; i++) {
68262306a36Sopenharmony_ci		edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
68362306a36Sopenharmony_ci		if (IS_ERR(edev[i])) {
68462306a36Sopenharmony_ci			dev_err(&pdev->dev,
68562306a36Sopenharmony_ci				"failed to add devfreq-event device\n");
68662306a36Sopenharmony_ci			return PTR_ERR(edev[i]);
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
69062306a36Sopenharmony_ci			dev_name(&pdev->dev), desc[i].name);
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	ret = clk_prepare_enable(info->ppmu.clk);
69462306a36Sopenharmony_ci	if (ret) {
69562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
69662306a36Sopenharmony_ci		return ret;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return 0;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic int exynos_ppmu_remove(struct platform_device *pdev)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct exynos_ppmu *info = platform_get_drvdata(pdev);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	clk_disable_unprepare(info->ppmu.clk);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic struct platform_driver exynos_ppmu_driver = {
71262306a36Sopenharmony_ci	.probe	= exynos_ppmu_probe,
71362306a36Sopenharmony_ci	.remove	= exynos_ppmu_remove,
71462306a36Sopenharmony_ci	.driver = {
71562306a36Sopenharmony_ci		.name	= "exynos-ppmu",
71662306a36Sopenharmony_ci		.of_match_table = exynos_ppmu_id_match,
71762306a36Sopenharmony_ci	},
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_cimodule_platform_driver(exynos_ppmu_driver);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ciMODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
72262306a36Sopenharmony_ciMODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
72362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
724