diff options
author | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 13:46:28 -0400 |
---|---|---|
committer | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 14:50:15 -0400 |
commit | daa22703f14c007e93b464c45fa60019a36f546d (patch) | |
tree | a1a130b6e128dc9d57c35c026977e1b4953105e1 /arch/arm/mach-exynos/ppmu.c | |
parent | 5aa287dcf1b5879aa0150b0511833c52885f5b4c (diff) |
Apply k4412 kernel from HardKernel for ODROID-X.
Diffstat (limited to 'arch/arm/mach-exynos/ppmu.c')
-rw-r--r-- | arch/arm/mach-exynos/ppmu.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/ppmu.c b/arch/arm/mach-exynos/ppmu.c new file mode 100644 index 00000000000..a46421798c2 --- /dev/null +++ b/arch/arm/mach-exynos/ppmu.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* linux/arch/arm/mach-exynos/ppmu.c | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * EXYNOS4 - CPU PPMU support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/math64.h> | ||
19 | |||
20 | #include <plat/cpu.h> | ||
21 | |||
22 | #include <mach/map.h> | ||
23 | #include <mach/regs-clock.h> | ||
24 | #include <mach/ppmu.h> | ||
25 | |||
26 | static LIST_HEAD(ppmu_list); | ||
27 | |||
28 | unsigned long long ppmu_load[PPMU_END]; | ||
29 | |||
30 | void exynos4_ppmu_reset(struct exynos4_ppmu_hw *ppmu) | ||
31 | { | ||
32 | void __iomem *ppmu_base = ppmu->hw_base; | ||
33 | int i; | ||
34 | |||
35 | __raw_writel(0x3 << 1, ppmu_base); | ||
36 | __raw_writel(0x8000000f, ppmu_base + PPMU_CNTENS); | ||
37 | |||
38 | if (soc_is_exynos4210()) | ||
39 | for (i = 0; i < NUMBER_OF_COUNTER; i++) { | ||
40 | __raw_writel(0x0, ppmu_base + DEVT0_ID + (i * DEVT_ID_OFFSET)); | ||
41 | __raw_writel(0x0, ppmu_base + DEVT0_IDMSK + (i * DEVT_ID_OFFSET)); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | void exynos4_ppmu_setevent(struct exynos4_ppmu_hw *ppmu, | ||
46 | unsigned int evt_num) | ||
47 | { | ||
48 | void __iomem *ppmu_base = ppmu->hw_base; | ||
49 | __raw_writel(ppmu->event[evt_num], ppmu_base + PPMU_BEVT0SEL + (evt_num * PPMU_BEVTSEL_OFFSET)); | ||
50 | } | ||
51 | |||
52 | void exynos4_ppmu_start(struct exynos4_ppmu_hw *ppmu) | ||
53 | { | ||
54 | void __iomem *ppmu_base = ppmu->hw_base; | ||
55 | __raw_writel(0x1, ppmu_base); | ||
56 | } | ||
57 | |||
58 | void exynos4_ppmu_stop(struct exynos4_ppmu_hw *ppmu) | ||
59 | { | ||
60 | void __iomem *ppmu_base = ppmu->hw_base; | ||
61 | __raw_writel(0x0, ppmu_base); | ||
62 | } | ||
63 | |||
64 | unsigned long long exynos4_ppmu_update(struct exynos4_ppmu_hw *ppmu, int ch) | ||
65 | { | ||
66 | void __iomem *ppmu_base = ppmu->hw_base; | ||
67 | unsigned long long total = 0; | ||
68 | |||
69 | ppmu->ccnt = __raw_readl(ppmu_base + PPMU_CCNT); | ||
70 | |||
71 | if (ppmu->ccnt == 0) | ||
72 | ppmu->ccnt = MAX_CCNT; | ||
73 | |||
74 | if (ch >= NUMBER_OF_COUNTER || ppmu->event[ch] == 0) | ||
75 | return 0; | ||
76 | |||
77 | if (ch == 3 && !soc_is_exynos4210()) | ||
78 | total = (((u64)__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) | | ||
79 | __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1))); | ||
80 | else | ||
81 | total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch)); | ||
82 | |||
83 | if (total > ppmu->ccnt) | ||
84 | total = ppmu->ccnt; | ||
85 | |||
86 | return div64_u64((total * ppmu->weight * 100), ppmu->ccnt); | ||
87 | } | ||
88 | |||
89 | void ppmu_start(struct device *dev) | ||
90 | { | ||
91 | struct exynos4_ppmu_hw *ppmu; | ||
92 | |||
93 | list_for_each_entry(ppmu, &ppmu_list, node) | ||
94 | if (ppmu->dev == dev) | ||
95 | exynos4_ppmu_start(ppmu); | ||
96 | } | ||
97 | |||
98 | void ppmu_update(struct device *dev, int ch) | ||
99 | { | ||
100 | struct exynos4_ppmu_hw *ppmu; | ||
101 | |||
102 | list_for_each_entry(ppmu, &ppmu_list, node) | ||
103 | if (ppmu->dev == dev) { | ||
104 | exynos4_ppmu_stop(ppmu); | ||
105 | ppmu_load[ppmu->id] = exynos4_ppmu_update(ppmu, ch); | ||
106 | exynos4_ppmu_reset(ppmu); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | void ppmu_reset(struct device *dev) | ||
111 | { | ||
112 | struct exynos4_ppmu_hw *ppmu; | ||
113 | int i; | ||
114 | |||
115 | list_for_each_entry(ppmu, &ppmu_list, node) { | ||
116 | if (ppmu->dev == dev) { | ||
117 | exynos4_ppmu_stop(ppmu); | ||
118 | for (i = 0; i < NUMBER_OF_COUNTER; i++) | ||
119 | if (ppmu->event[i] != 0) | ||
120 | exynos4_ppmu_setevent(ppmu, i); | ||
121 | exynos4_ppmu_reset(ppmu); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | void ppmu_init(struct exynos4_ppmu_hw *ppmu, struct device *dev) | ||
127 | { | ||
128 | void __iomem *ppmu_base = ppmu->hw_base; | ||
129 | int i; | ||
130 | |||
131 | ppmu->dev = dev; | ||
132 | list_add(&ppmu->node, &ppmu_list); | ||
133 | |||
134 | if (soc_is_exynos4210()) | ||
135 | for (i = 0; i < NUMBER_OF_COUNTER; i++) { | ||
136 | __raw_writel(0x0, ppmu_base + DEVT0_ID + (i * DEVT_ID_OFFSET)); | ||
137 | __raw_writel(0x0, ppmu_base + DEVT0_IDMSK + (i * DEVT_ID_OFFSET)); | ||
138 | } | ||
139 | |||
140 | for (i = 0; i < NUMBER_OF_COUNTER; i++) | ||
141 | if (ppmu->event[i] != 0) | ||
142 | exynos4_ppmu_setevent(ppmu, i); | ||
143 | } | ||
144 | |||
145 | struct exynos4_ppmu_hw exynos_ppmu[] = { | ||
146 | [PPMU_DMC0] = { | ||
147 | .id = PPMU_DMC0, | ||
148 | .hw_base = S5P_VA_PPMU_DMC0, | ||
149 | .event[3] = RDWR_DATA_COUNT, | ||
150 | .weight = DEFAULT_WEIGHT, | ||
151 | }, | ||
152 | [PPMU_DMC1] = { | ||
153 | .id = PPMU_DMC1, | ||
154 | .hw_base = S5P_VA_PPMU_DMC1, | ||
155 | .event[3] = RDWR_DATA_COUNT, | ||
156 | .weight = DEFAULT_WEIGHT, | ||
157 | }, | ||
158 | [PPMU_CPU] = { | ||
159 | .id = PPMU_CPU, | ||
160 | .hw_base = S5P_VA_PPMU_CPU, | ||
161 | .event[3] = RDWR_DATA_COUNT, | ||
162 | .weight = DEFAULT_WEIGHT, | ||
163 | }, | ||
164 | #ifdef CONFIG_ARCH_EXYNOS5 | ||
165 | [PPMU_DDR_C] = { | ||
166 | .id = PPMU_DDR_C, | ||
167 | .hw_base = S5P_VA_PPMU_DDR_C, | ||
168 | .event[3] = RDWR_DATA_COUNT, | ||
169 | .weight = DEFAULT_WEIGHT, | ||
170 | }, | ||
171 | [PPMU_DDR_R1] = { | ||
172 | .id = PPMU_DDR_R1, | ||
173 | .hw_base = S5P_VA_PPMU_DDR_R1, | ||
174 | .event[3] = RDWR_DATA_COUNT, | ||
175 | .weight = DEFAULT_WEIGHT, | ||
176 | }, | ||
177 | [PPMU_DDR_L] = { | ||
178 | .id = PPMU_DDR_L, | ||
179 | .hw_base = S5P_VA_PPMU_DDR_L, | ||
180 | .event[3] = RDWR_DATA_COUNT, | ||
181 | .weight = DEFAULT_WEIGHT, | ||
182 | }, | ||
183 | [PPMU_RIGHT0_BUS] = { | ||
184 | .id = PPMU_RIGHT0_BUS, | ||
185 | .hw_base = S5P_VA_PPMU_RIGHT0_BUS, | ||
186 | .event[3] = RDWR_DATA_COUNT, | ||
187 | .weight = DEFAULT_WEIGHT, | ||
188 | }, | ||
189 | #endif | ||
190 | }; | ||