18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Author : Chanwoo Choi <cw00.choi@samsung.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/regmap.h>
198c2ecf20Sopenharmony_ci#include <linux/suspend.h>
208c2ecf20Sopenharmony_ci#include <linux/devfreq-event.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "exynos-ppmu.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cienum exynos_ppmu_type {
258c2ecf20Sopenharmony_ci	EXYNOS_TYPE_PPMU,
268c2ecf20Sopenharmony_ci	EXYNOS_TYPE_PPMU_V2,
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct exynos_ppmu_data {
308c2ecf20Sopenharmony_ci	struct clk *clk;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct exynos_ppmu {
348c2ecf20Sopenharmony_ci	struct devfreq_event_dev **edev;
358c2ecf20Sopenharmony_ci	struct devfreq_event_desc *desc;
368c2ecf20Sopenharmony_ci	unsigned int num_events;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	struct device *dev;
398c2ecf20Sopenharmony_ci	struct regmap *regmap;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	struct exynos_ppmu_data ppmu;
428c2ecf20Sopenharmony_ci	enum exynos_ppmu_type ppmu_type;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define PPMU_EVENT(name)			\
468c2ecf20Sopenharmony_ci	{ "ppmu-event0-"#name, PPMU_PMNCNT0 },	\
478c2ecf20Sopenharmony_ci	{ "ppmu-event1-"#name, PPMU_PMNCNT1 },	\
488c2ecf20Sopenharmony_ci	{ "ppmu-event2-"#name, PPMU_PMNCNT2 },	\
498c2ecf20Sopenharmony_ci	{ "ppmu-event3-"#name, PPMU_PMNCNT3 }
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct __exynos_ppmu_events {
528c2ecf20Sopenharmony_ci	char *name;
538c2ecf20Sopenharmony_ci	int id;
548c2ecf20Sopenharmony_ci} ppmu_events[] = {
558c2ecf20Sopenharmony_ci	/* For Exynos3250, Exynos4 and Exynos5260 */
568c2ecf20Sopenharmony_ci	PPMU_EVENT(g3d),
578c2ecf20Sopenharmony_ci	PPMU_EVENT(fsys),
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* For Exynos4 SoCs and Exynos3250 */
608c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc0),
618c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc1),
628c2ecf20Sopenharmony_ci	PPMU_EVENT(cpu),
638c2ecf20Sopenharmony_ci	PPMU_EVENT(rightbus),
648c2ecf20Sopenharmony_ci	PPMU_EVENT(leftbus),
658c2ecf20Sopenharmony_ci	PPMU_EVENT(lcd0),
668c2ecf20Sopenharmony_ci	PPMU_EVENT(camif),
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* Only for Exynos3250 and Exynos5260 */
698c2ecf20Sopenharmony_ci	PPMU_EVENT(mfc),
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* Only for Exynos4 SoCs */
728c2ecf20Sopenharmony_ci	PPMU_EVENT(mfc-left),
738c2ecf20Sopenharmony_ci	PPMU_EVENT(mfc-right),
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Only for Exynos5260 SoCs */
768c2ecf20Sopenharmony_ci	PPMU_EVENT(drex0-s0),
778c2ecf20Sopenharmony_ci	PPMU_EVENT(drex0-s1),
788c2ecf20Sopenharmony_ci	PPMU_EVENT(drex1-s0),
798c2ecf20Sopenharmony_ci	PPMU_EVENT(drex1-s1),
808c2ecf20Sopenharmony_ci	PPMU_EVENT(eagle),
818c2ecf20Sopenharmony_ci	PPMU_EVENT(kfc),
828c2ecf20Sopenharmony_ci	PPMU_EVENT(isp),
838c2ecf20Sopenharmony_ci	PPMU_EVENT(fimc),
848c2ecf20Sopenharmony_ci	PPMU_EVENT(gscl),
858c2ecf20Sopenharmony_ci	PPMU_EVENT(mscl),
868c2ecf20Sopenharmony_ci	PPMU_EVENT(fimd0x),
878c2ecf20Sopenharmony_ci	PPMU_EVENT(fimd1x),
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Only for Exynos5433 SoCs */
908c2ecf20Sopenharmony_ci	PPMU_EVENT(d0-cpu),
918c2ecf20Sopenharmony_ci	PPMU_EVENT(d0-general),
928c2ecf20Sopenharmony_ci	PPMU_EVENT(d0-rt),
938c2ecf20Sopenharmony_ci	PPMU_EVENT(d1-cpu),
948c2ecf20Sopenharmony_ci	PPMU_EVENT(d1-general),
958c2ecf20Sopenharmony_ci	PPMU_EVENT(d1-rt),
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* For Exynos5422 SoC */
988c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc0_0),
998c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc0_1),
1008c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc1_0),
1018c2ecf20Sopenharmony_ci	PPMU_EVENT(dmc1_1),
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int __exynos_ppmu_find_ppmu_id(const char *edev_name)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
1098c2ecf20Sopenharmony_ci		if (!strcmp(edev_name, ppmu_events[i].name))
1108c2ecf20Sopenharmony_ci			return ppmu_events[i].id;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return -EINVAL;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return __exynos_ppmu_find_ppmu_id(edev->desc->name);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/*
1218c2ecf20Sopenharmony_ci * The devfreq-event ops structure for PPMU v1.1
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_cistatic int exynos_ppmu_disable(struct devfreq_event_dev *edev)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
1268c2ecf20Sopenharmony_ci	int ret;
1278c2ecf20Sopenharmony_ci	u32 pmnc;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Disable all counters */
1308c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENC,
1318c2ecf20Sopenharmony_ci				PPMU_CCNT_MASK |
1328c2ecf20Sopenharmony_ci				PPMU_PMCNT0_MASK |
1338c2ecf20Sopenharmony_ci				PPMU_PMCNT1_MASK |
1348c2ecf20Sopenharmony_ci				PPMU_PMCNT2_MASK |
1358c2ecf20Sopenharmony_ci				PPMU_PMCNT3_MASK);
1368c2ecf20Sopenharmony_ci	if (ret < 0)
1378c2ecf20Sopenharmony_ci		return ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Disable PPMU */
1408c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
1418c2ecf20Sopenharmony_ci	if (ret < 0)
1428c2ecf20Sopenharmony_ci		return ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
1458c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
1468c2ecf20Sopenharmony_ci	if (ret < 0)
1478c2ecf20Sopenharmony_ci		return ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
1558c2ecf20Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
1568c2ecf20Sopenharmony_ci	int ret;
1578c2ecf20Sopenharmony_ci	u32 pmnc, cntens;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (id < 0)
1608c2ecf20Sopenharmony_ci		return id;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Enable specific counter */
1638c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
1648c2ecf20Sopenharmony_ci	if (ret < 0)
1658c2ecf20Sopenharmony_ci		return ret;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
1688c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
1698c2ecf20Sopenharmony_ci	if (ret < 0)
1708c2ecf20Sopenharmony_ci		return ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* Set the event of proper data type monitoring */
1738c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
1748c2ecf20Sopenharmony_ci			   edev->desc->event_type);
1758c2ecf20Sopenharmony_ci	if (ret < 0)
1768c2ecf20Sopenharmony_ci		return ret;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Reset cycle counter/performance counter and enable PPMU */
1798c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
1808c2ecf20Sopenharmony_ci	if (ret < 0)
1818c2ecf20Sopenharmony_ci		return ret;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
1848c2ecf20Sopenharmony_ci			| PPMU_PMNC_COUNTER_RESET_MASK
1858c2ecf20Sopenharmony_ci			| PPMU_PMNC_CC_RESET_MASK);
1868c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
1878c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
1888c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
1898c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
1908c2ecf20Sopenharmony_ci	if (ret < 0)
1918c2ecf20Sopenharmony_ci		return ret;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
1978c2ecf20Sopenharmony_ci				struct devfreq_event_data *edata)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
2008c2ecf20Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
2018c2ecf20Sopenharmony_ci	unsigned int total_count, load_count;
2028c2ecf20Sopenharmony_ci	unsigned int pmcnt3_high, pmcnt3_low;
2038c2ecf20Sopenharmony_ci	unsigned int pmnc, cntenc;
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (id < 0)
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Disable PPMU */
2108c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
2118c2ecf20Sopenharmony_ci	if (ret < 0)
2128c2ecf20Sopenharmony_ci		return ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
2158c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
2168c2ecf20Sopenharmony_ci	if (ret < 0)
2178c2ecf20Sopenharmony_ci		return ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* Read cycle count */
2208c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
2218c2ecf20Sopenharmony_ci	if (ret < 0)
2228c2ecf20Sopenharmony_ci		return ret;
2238c2ecf20Sopenharmony_ci	edata->total_count = total_count;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Read performance count */
2268c2ecf20Sopenharmony_ci	switch (id) {
2278c2ecf20Sopenharmony_ci	case PPMU_PMNCNT0:
2288c2ecf20Sopenharmony_ci	case PPMU_PMNCNT1:
2298c2ecf20Sopenharmony_ci	case PPMU_PMNCNT2:
2308c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
2318c2ecf20Sopenharmony_ci		if (ret < 0)
2328c2ecf20Sopenharmony_ci			return ret;
2338c2ecf20Sopenharmony_ci		edata->load_count = load_count;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	case PPMU_PMNCNT3:
2368c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
2378c2ecf20Sopenharmony_ci		if (ret < 0)
2388c2ecf20Sopenharmony_ci			return ret;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
2418c2ecf20Sopenharmony_ci		if (ret < 0)
2428c2ecf20Sopenharmony_ci			return ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	default:
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Disable specific counter */
2518c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
2528c2ecf20Sopenharmony_ci	if (ret < 0)
2538c2ecf20Sopenharmony_ci		return ret;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
2568c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
2578c2ecf20Sopenharmony_ci	if (ret < 0)
2588c2ecf20Sopenharmony_ci		return ret;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
2618c2ecf20Sopenharmony_ci					edata->load_count, edata->total_count);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return 0;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic const struct devfreq_event_ops exynos_ppmu_ops = {
2678c2ecf20Sopenharmony_ci	.disable = exynos_ppmu_disable,
2688c2ecf20Sopenharmony_ci	.set_event = exynos_ppmu_set_event,
2698c2ecf20Sopenharmony_ci	.get_event = exynos_ppmu_get_event,
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * The devfreq-event ops structure for PPMU v2.0
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_cistatic int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
2788c2ecf20Sopenharmony_ci	int ret;
2798c2ecf20Sopenharmony_ci	u32 pmnc, clear;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Disable all counters */
2828c2ecf20Sopenharmony_ci	clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
2838c2ecf20Sopenharmony_ci		| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
2848c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
2858c2ecf20Sopenharmony_ci	if (ret < 0)
2868c2ecf20Sopenharmony_ci		return ret;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
2898c2ecf20Sopenharmony_ci	if (ret < 0)
2908c2ecf20Sopenharmony_ci		return ret;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
2938c2ecf20Sopenharmony_ci	if (ret < 0)
2948c2ecf20Sopenharmony_ci		return ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
2978c2ecf20Sopenharmony_ci	if (ret < 0)
2988c2ecf20Sopenharmony_ci		return ret;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
3018c2ecf20Sopenharmony_ci	if (ret < 0)
3028c2ecf20Sopenharmony_ci		return ret;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
3058c2ecf20Sopenharmony_ci	if (ret < 0)
3068c2ecf20Sopenharmony_ci		return ret;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
3098c2ecf20Sopenharmony_ci	if (ret < 0)
3108c2ecf20Sopenharmony_ci		return ret;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
3138c2ecf20Sopenharmony_ci	if (ret < 0)
3148c2ecf20Sopenharmony_ci		return ret;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
3178c2ecf20Sopenharmony_ci	if (ret < 0)
3188c2ecf20Sopenharmony_ci		return ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
3218c2ecf20Sopenharmony_ci	if (ret < 0)
3228c2ecf20Sopenharmony_ci		return ret;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
3258c2ecf20Sopenharmony_ci	if (ret < 0)
3268c2ecf20Sopenharmony_ci		return ret;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
3298c2ecf20Sopenharmony_ci	if (ret < 0)
3308c2ecf20Sopenharmony_ci		return ret;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
3338c2ecf20Sopenharmony_ci	if (ret < 0)
3348c2ecf20Sopenharmony_ci		return ret;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
3378c2ecf20Sopenharmony_ci	if (ret < 0)
3388c2ecf20Sopenharmony_ci		return ret;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
3418c2ecf20Sopenharmony_ci	if (ret < 0)
3428c2ecf20Sopenharmony_ci		return ret;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
3458c2ecf20Sopenharmony_ci	if (ret < 0)
3468c2ecf20Sopenharmony_ci		return ret;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
3498c2ecf20Sopenharmony_ci	if (ret < 0)
3508c2ecf20Sopenharmony_ci		return ret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
3538c2ecf20Sopenharmony_ci	if (ret < 0)
3548c2ecf20Sopenharmony_ci		return ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Disable PPMU */
3578c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
3588c2ecf20Sopenharmony_ci	if (ret < 0)
3598c2ecf20Sopenharmony_ci		return ret;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
3628c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
3638c2ecf20Sopenharmony_ci	if (ret < 0)
3648c2ecf20Sopenharmony_ci		return ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
3728c2ecf20Sopenharmony_ci	unsigned int pmnc, cntens;
3738c2ecf20Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
3748c2ecf20Sopenharmony_ci	int ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Enable all counters */
3778c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
3788c2ecf20Sopenharmony_ci	if (ret < 0)
3798c2ecf20Sopenharmony_ci		return ret;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
3828c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
3838c2ecf20Sopenharmony_ci	if (ret < 0)
3848c2ecf20Sopenharmony_ci		return ret;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Set the event of proper data type monitoring */
3878c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
3888c2ecf20Sopenharmony_ci			   edev->desc->event_type);
3898c2ecf20Sopenharmony_ci	if (ret < 0)
3908c2ecf20Sopenharmony_ci		return ret;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Reset cycle counter/performance counter and enable PPMU */
3938c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
3948c2ecf20Sopenharmony_ci	if (ret < 0)
3958c2ecf20Sopenharmony_ci		return ret;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	pmnc &= ~(PPMU_PMNC_ENABLE_MASK
3988c2ecf20Sopenharmony_ci			| PPMU_PMNC_COUNTER_RESET_MASK
3998c2ecf20Sopenharmony_ci			| PPMU_PMNC_CC_RESET_MASK
4008c2ecf20Sopenharmony_ci			| PPMU_PMNC_CC_DIVIDER_MASK
4018c2ecf20Sopenharmony_ci			| PPMU_V2_PMNC_START_MODE_MASK);
4028c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
4038c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
4048c2ecf20Sopenharmony_ci	pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
4058c2ecf20Sopenharmony_ci	pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
4088c2ecf20Sopenharmony_ci	if (ret < 0)
4098c2ecf20Sopenharmony_ci		return ret;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
4158c2ecf20Sopenharmony_ci				    struct devfreq_event_data *edata)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
4188c2ecf20Sopenharmony_ci	int id = exynos_ppmu_find_ppmu_id(edev);
4198c2ecf20Sopenharmony_ci	int ret;
4208c2ecf20Sopenharmony_ci	unsigned int pmnc, cntenc;
4218c2ecf20Sopenharmony_ci	unsigned int pmcnt_high, pmcnt_low;
4228c2ecf20Sopenharmony_ci	unsigned int total_count, count;
4238c2ecf20Sopenharmony_ci	unsigned long load_count = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Disable PPMU */
4268c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
4278c2ecf20Sopenharmony_ci	if (ret < 0)
4288c2ecf20Sopenharmony_ci		return ret;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	pmnc &= ~PPMU_PMNC_ENABLE_MASK;
4318c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
4328c2ecf20Sopenharmony_ci	if (ret < 0)
4338c2ecf20Sopenharmony_ci		return ret;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Read cycle count and performance count */
4368c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
4378c2ecf20Sopenharmony_ci	if (ret < 0)
4388c2ecf20Sopenharmony_ci		return ret;
4398c2ecf20Sopenharmony_ci	edata->total_count = total_count;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	switch (id) {
4428c2ecf20Sopenharmony_ci	case PPMU_PMNCNT0:
4438c2ecf20Sopenharmony_ci	case PPMU_PMNCNT1:
4448c2ecf20Sopenharmony_ci	case PPMU_PMNCNT2:
4458c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
4468c2ecf20Sopenharmony_ci		if (ret < 0)
4478c2ecf20Sopenharmony_ci			return ret;
4488c2ecf20Sopenharmony_ci		load_count = count;
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci	case PPMU_PMNCNT3:
4518c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
4528c2ecf20Sopenharmony_ci						&pmcnt_high);
4538c2ecf20Sopenharmony_ci		if (ret < 0)
4548c2ecf20Sopenharmony_ci			return ret;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
4578c2ecf20Sopenharmony_ci		if (ret < 0)
4588c2ecf20Sopenharmony_ci			return ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci	edata->load_count = load_count;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* Disable all counters */
4668c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
4678c2ecf20Sopenharmony_ci	if (ret < 0)
4688c2ecf20Sopenharmony_ci		return 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
4718c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
4728c2ecf20Sopenharmony_ci	if (ret < 0)
4738c2ecf20Sopenharmony_ci		return ret;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
4768c2ecf20Sopenharmony_ci					edata->load_count, edata->total_count);
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic const struct devfreq_event_ops exynos_ppmu_v2_ops = {
4818c2ecf20Sopenharmony_ci	.disable = exynos_ppmu_v2_disable,
4828c2ecf20Sopenharmony_ci	.set_event = exynos_ppmu_v2_set_event,
4838c2ecf20Sopenharmony_ci	.get_event = exynos_ppmu_v2_get_event,
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_ppmu_id_match[] = {
4878c2ecf20Sopenharmony_ci	{
4888c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos-ppmu",
4898c2ecf20Sopenharmony_ci		.data = (void *)EXYNOS_TYPE_PPMU,
4908c2ecf20Sopenharmony_ci	}, {
4918c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos-ppmu-v2",
4928c2ecf20Sopenharmony_ci		.data = (void *)EXYNOS_TYPE_PPMU_V2,
4938c2ecf20Sopenharmony_ci	},
4948c2ecf20Sopenharmony_ci	{ /* sentinel */ },
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int of_get_devfreq_events(struct device_node *np,
4998c2ecf20Sopenharmony_ci				 struct exynos_ppmu *info)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct devfreq_event_desc *desc;
5028c2ecf20Sopenharmony_ci	struct device *dev = info->dev;
5038c2ecf20Sopenharmony_ci	struct device_node *events_np, *node;
5048c2ecf20Sopenharmony_ci	int i, j, count;
5058c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
5068c2ecf20Sopenharmony_ci	int ret;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	events_np = of_get_child_by_name(np, "events");
5098c2ecf20Sopenharmony_ci	if (!events_np) {
5108c2ecf20Sopenharmony_ci		dev_err(dev,
5118c2ecf20Sopenharmony_ci			"failed to get child node of devfreq-event devices\n");
5128c2ecf20Sopenharmony_ci		return -EINVAL;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	count = of_get_child_count(events_np);
5168c2ecf20Sopenharmony_ci	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
5178c2ecf20Sopenharmony_ci	if (!desc) {
5188c2ecf20Sopenharmony_ci		of_node_put(events_np);
5198c2ecf20Sopenharmony_ci		return -ENOMEM;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci	info->num_events = count;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	of_id = of_match_device(exynos_ppmu_id_match, dev);
5248c2ecf20Sopenharmony_ci	if (of_id)
5258c2ecf20Sopenharmony_ci		info->ppmu_type = (enum exynos_ppmu_type)of_id->data;
5268c2ecf20Sopenharmony_ci	else {
5278c2ecf20Sopenharmony_ci		of_node_put(events_np);
5288c2ecf20Sopenharmony_ci		return -EINVAL;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	j = 0;
5328c2ecf20Sopenharmony_ci	for_each_child_of_node(events_np, node) {
5338c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
5348c2ecf20Sopenharmony_ci			if (!ppmu_events[i].name)
5358c2ecf20Sopenharmony_ci				continue;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci			if (of_node_name_eq(node, ppmu_events[i].name))
5388c2ecf20Sopenharmony_ci				break;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		if (i == ARRAY_SIZE(ppmu_events)) {
5428c2ecf20Sopenharmony_ci			dev_warn(dev,
5438c2ecf20Sopenharmony_ci				"don't know how to configure events : %pOFn\n",
5448c2ecf20Sopenharmony_ci				node);
5458c2ecf20Sopenharmony_ci			continue;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		switch (info->ppmu_type) {
5498c2ecf20Sopenharmony_ci		case EXYNOS_TYPE_PPMU:
5508c2ecf20Sopenharmony_ci			desc[j].ops = &exynos_ppmu_ops;
5518c2ecf20Sopenharmony_ci			break;
5528c2ecf20Sopenharmony_ci		case EXYNOS_TYPE_PPMU_V2:
5538c2ecf20Sopenharmony_ci			desc[j].ops = &exynos_ppmu_v2_ops;
5548c2ecf20Sopenharmony_ci			break;
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		desc[j].driver_data = info;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		of_property_read_string(node, "event-name", &desc[j].name);
5608c2ecf20Sopenharmony_ci		ret = of_property_read_u32(node, "event-data-type",
5618c2ecf20Sopenharmony_ci					   &desc[j].event_type);
5628c2ecf20Sopenharmony_ci		if (ret) {
5638c2ecf20Sopenharmony_ci			/* Set the event of proper data type counting.
5648c2ecf20Sopenharmony_ci			 * Check if the data type has been defined in DT,
5658c2ecf20Sopenharmony_ci			 * use default if not.
5668c2ecf20Sopenharmony_ci			 */
5678c2ecf20Sopenharmony_ci			if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {
5688c2ecf20Sopenharmony_ci				int id;
5698c2ecf20Sopenharmony_ci				/* Not all registers take the same value for
5708c2ecf20Sopenharmony_ci				 * read+write data count.
5718c2ecf20Sopenharmony_ci				 */
5728c2ecf20Sopenharmony_ci				id = __exynos_ppmu_find_ppmu_id(desc[j].name);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci				switch (id) {
5758c2ecf20Sopenharmony_ci				case PPMU_PMNCNT0:
5768c2ecf20Sopenharmony_ci				case PPMU_PMNCNT1:
5778c2ecf20Sopenharmony_ci				case PPMU_PMNCNT2:
5788c2ecf20Sopenharmony_ci					desc[j].event_type = PPMU_V2_RO_DATA_CNT
5798c2ecf20Sopenharmony_ci						| PPMU_V2_WO_DATA_CNT;
5808c2ecf20Sopenharmony_ci					break;
5818c2ecf20Sopenharmony_ci				case PPMU_PMNCNT3:
5828c2ecf20Sopenharmony_ci					desc[j].event_type =
5838c2ecf20Sopenharmony_ci						PPMU_V2_EVT3_RW_DATA_CNT;
5848c2ecf20Sopenharmony_ci					break;
5858c2ecf20Sopenharmony_ci				}
5868c2ecf20Sopenharmony_ci			} else {
5878c2ecf20Sopenharmony_ci				desc[j].event_type = PPMU_RO_DATA_CNT |
5888c2ecf20Sopenharmony_ci					PPMU_WO_DATA_CNT;
5898c2ecf20Sopenharmony_ci			}
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		j++;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	info->desc = desc;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	of_node_put(events_np);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return 0;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic struct regmap_config exynos_ppmu_regmap_config = {
6028c2ecf20Sopenharmony_ci	.reg_bits = 32,
6038c2ecf20Sopenharmony_ci	.val_bits = 32,
6048c2ecf20Sopenharmony_ci	.reg_stride = 4,
6058c2ecf20Sopenharmony_ci};
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int exynos_ppmu_parse_dt(struct platform_device *pdev,
6088c2ecf20Sopenharmony_ci				struct exynos_ppmu *info)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct device *dev = info->dev;
6118c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
6128c2ecf20Sopenharmony_ci	struct resource *res;
6138c2ecf20Sopenharmony_ci	void __iomem *base;
6148c2ecf20Sopenharmony_ci	int ret = 0;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (!np) {
6178c2ecf20Sopenharmony_ci		dev_err(dev, "failed to find devicetree node\n");
6188c2ecf20Sopenharmony_ci		return -EINVAL;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* Maps the memory mapped IO to control PPMU register */
6228c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6238c2ecf20Sopenharmony_ci	base = devm_ioremap_resource(dev, res);
6248c2ecf20Sopenharmony_ci	if (IS_ERR(base))
6258c2ecf20Sopenharmony_ci		return PTR_ERR(base);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
6288c2ecf20Sopenharmony_ci	info->regmap = devm_regmap_init_mmio(dev, base,
6298c2ecf20Sopenharmony_ci					&exynos_ppmu_regmap_config);
6308c2ecf20Sopenharmony_ci	if (IS_ERR(info->regmap)) {
6318c2ecf20Sopenharmony_ci		dev_err(dev, "failed to initialize regmap\n");
6328c2ecf20Sopenharmony_ci		return PTR_ERR(info->regmap);
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	info->ppmu.clk = devm_clk_get(dev, "ppmu");
6368c2ecf20Sopenharmony_ci	if (IS_ERR(info->ppmu.clk)) {
6378c2ecf20Sopenharmony_ci		info->ppmu.clk = NULL;
6388c2ecf20Sopenharmony_ci		dev_warn(dev, "cannot get PPMU clock\n");
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	ret = of_get_devfreq_events(np, info);
6428c2ecf20Sopenharmony_ci	if (ret < 0) {
6438c2ecf20Sopenharmony_ci		dev_err(dev, "failed to parse exynos ppmu dt node\n");
6448c2ecf20Sopenharmony_ci		return ret;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return 0;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic int exynos_ppmu_probe(struct platform_device *pdev)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	struct exynos_ppmu *info;
6538c2ecf20Sopenharmony_ci	struct devfreq_event_dev **edev;
6548c2ecf20Sopenharmony_ci	struct devfreq_event_desc *desc;
6558c2ecf20Sopenharmony_ci	int i, ret = 0, size;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
6588c2ecf20Sopenharmony_ci	if (!info)
6598c2ecf20Sopenharmony_ci		return -ENOMEM;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	info->dev = &pdev->dev;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Parse dt data to get resource */
6648c2ecf20Sopenharmony_ci	ret = exynos_ppmu_parse_dt(pdev, info);
6658c2ecf20Sopenharmony_ci	if (ret < 0) {
6668c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
6678c2ecf20Sopenharmony_ci			"failed to parse devicetree for resource\n");
6688c2ecf20Sopenharmony_ci		return ret;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci	desc = info->desc;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	size = sizeof(struct devfreq_event_dev *) * info->num_events;
6738c2ecf20Sopenharmony_ci	info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
6748c2ecf20Sopenharmony_ci	if (!info->edev)
6758c2ecf20Sopenharmony_ci		return -ENOMEM;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	edev = info->edev;
6788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, info);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	for (i = 0; i < info->num_events; i++) {
6818c2ecf20Sopenharmony_ci		edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
6828c2ecf20Sopenharmony_ci		if (IS_ERR(edev[i])) {
6838c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
6848c2ecf20Sopenharmony_ci				"failed to add devfreq-event device\n");
6858c2ecf20Sopenharmony_ci			return PTR_ERR(edev[i]);
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
6898c2ecf20Sopenharmony_ci			dev_name(&pdev->dev), desc[i].name);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(info->ppmu.clk);
6938c2ecf20Sopenharmony_ci	if (ret) {
6948c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
6958c2ecf20Sopenharmony_ci		return ret;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int exynos_ppmu_remove(struct platform_device *pdev)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct exynos_ppmu *info = platform_get_drvdata(pdev);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	clk_disable_unprepare(info->ppmu.clk);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	return 0;
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic struct platform_driver exynos_ppmu_driver = {
7118c2ecf20Sopenharmony_ci	.probe	= exynos_ppmu_probe,
7128c2ecf20Sopenharmony_ci	.remove	= exynos_ppmu_remove,
7138c2ecf20Sopenharmony_ci	.driver = {
7148c2ecf20Sopenharmony_ci		.name	= "exynos-ppmu",
7158c2ecf20Sopenharmony_ci		.of_match_table = exynos_ppmu_id_match,
7168c2ecf20Sopenharmony_ci	},
7178c2ecf20Sopenharmony_ci};
7188c2ecf20Sopenharmony_cimodule_platform_driver(exynos_ppmu_driver);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
7218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
7228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
723