1/*
2 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 *		http://www.samsung.com/
4 *
5 * EXYNOS - PPMU support
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/io.h>
15
16#include "exynos_ppmu.h"
17
18void exynos_ppmu_reset(void __iomem *ppmu_base)
19{
20	__raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base);
21	__raw_writel(PPMU_ENABLE_CYCLE  |
22		     PPMU_ENABLE_COUNT0 |
23		     PPMU_ENABLE_COUNT1 |
24		     PPMU_ENABLE_COUNT2 |
25		     PPMU_ENABLE_COUNT3,
26		     ppmu_base + PPMU_CNTENS);
27}
28
29void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
30			unsigned int evt)
31{
32	__raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch));
33}
34
35void exynos_ppmu_start(void __iomem *ppmu_base)
36{
37	__raw_writel(PPMU_ENABLE, ppmu_base);
38}
39
40void exynos_ppmu_stop(void __iomem *ppmu_base)
41{
42	__raw_writel(PPMU_DISABLE, ppmu_base);
43}
44
45unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch)
46{
47	unsigned int total;
48
49	if (ch == PPMU_PMNCNT3)
50		total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) |
51			  __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1)));
52	else
53		total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch));
54
55	return total;
56}
57
58void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data)
59{
60	unsigned int i;
61
62	for (i = 0; i < ppmu_data->ppmu_end; i++) {
63		void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
64
65		/* Reset the performance and cycle counters */
66		exynos_ppmu_reset(ppmu_base);
67
68		/* Setup count registers to monitor read/write transactions */
69		ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
70		exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
71					ppmu_data->ppmu[i].event[PPMU_PMNCNT3]);
72
73		exynos_ppmu_start(ppmu_base);
74	}
75}
76EXPORT_SYMBOL(busfreq_mon_reset);
77
78void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
79{
80	int i, j;
81
82	for (i = 0; i < ppmu_data->ppmu_end; i++) {
83		void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
84
85		exynos_ppmu_stop(ppmu_base);
86
87		/* Update local data from PPMU */
88		ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
89
90		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
91			if (ppmu_data->ppmu[i].event[j] == 0)
92				ppmu_data->ppmu[i].count[j] = 0;
93			else
94				ppmu_data->ppmu[i].count[j] =
95					exynos_ppmu_read(ppmu_base, j);
96		}
97	}
98
99	busfreq_mon_reset(ppmu_data);
100}
101EXPORT_SYMBOL(exynos_read_ppmu);
102
103int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
104{
105	unsigned int count = 0;
106	int i, j, busy = 0;
107
108	for (i = 0; i < ppmu_data->ppmu_end; i++) {
109		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
110			if (ppmu_data->ppmu[i].count[j] > count) {
111				count = ppmu_data->ppmu[i].count[j];
112				busy = i;
113			}
114		}
115	}
116
117	return busy;
118}
119EXPORT_SYMBOL(exynos_get_busier_ppmu);
120