blob: b08a9ec11cbdb0e195e11bbd3ecfff552decbe6d [file] [log] [blame]
Olav Haugan7a2f99c2013-02-04 14:43:26 -08001/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13/**
14 * This file contains the part of the IOMMUv0 PMU driver that actually touches
15 * IOMMU PMU registers.
16 */
17
18#include <linux/io.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/device.h>
22#include <mach/iommu_hw-v0.h>
23#include <mach/iommu_perfmon.h>
Olav Hauganeece7e52013-04-02 10:22:21 -070024#include <mach/iommu.h>
Olav Haugan7a2f99c2013-02-04 14:43:26 -080025
26#define PM_RESET_MASK (0xF)
27#define PM_RESET_SHIFT (0x8)
28#define PM_RESET (PM_RESET_MASK << PM_RESET_SHIFT)
29
30#define PM_ENABLE_MASK (0x1)
31#define PM_ENABLE_SHIFT (0x0)
32#define PM_ENABLE (PM_ENABLE_MASK << PM_ENABLE_SHIFT)
33
34#define PM_OVFL_FLAG_MASK (0xF)
35#define PM_OVFL_FLAG_SHIFT (0x0)
36#define PM_OVFL_FLAG (PM_OVFL_FLAG_MASK << PM_OVFL_FLAG_SHIFT)
37
38#define PM_EVENT_TYPE_MASK (0x1F)
39#define PM_EVENT_TYPE_SHIFT (0x2)
40#define PM_EVENT_TYPE (PM_EVENT_TYPE_MASK << PM_EVENT_TYPE_SHIFT)
41
42#define PM_INT_EN_MASK (0x1)
43#define PM_INT_EN_SHIFT (0x0)
44#define PM_INT_EN (PM_INT_EN_MASK << PM_INT_EN_SHIFT)
45
46#define PM_INT_POL_MASK (0x1)
47#define PM_INT_POL_SHIFT (0x2)
48#define PM_INT_ACTIVE_HIGH (0x1)
49
50#define PMEVCNTR_(n) (EMC_N + n*4)
51#define PMEVTYPER_(n) (EMCC_N + n*4)
52
53/**
54 * Translate between SMMUv0 event classes and standard ARM SMMU event classes
55 */
56static int iommu_pm_event_class_translation_table[] = {
57 MSM_IOMMU_PMU_NO_EVENT_CLASS,
58 MSM_IOMMU_PMU_NO_EVENT_CLASS,
59 MSM_IOMMU_PMU_NO_EVENT_CLASS,
60 0x8,
61 0x9,
62 MSM_IOMMU_PMU_NO_EVENT_CLASS,
63 0x80,
64 MSM_IOMMU_PMU_NO_EVENT_CLASS,
65 0x12,
66 MSM_IOMMU_PMU_NO_EVENT_CLASS,
67 MSM_IOMMU_PMU_NO_EVENT_CLASS,
68 MSM_IOMMU_PMU_NO_EVENT_CLASS,
69 MSM_IOMMU_PMU_NO_EVENT_CLASS,
70 MSM_IOMMU_PMU_NO_EVENT_CLASS,
71 MSM_IOMMU_PMU_NO_EVENT_CLASS,
72 0x10,
73};
74
75static int iommu_pm_translate_event_class(int event_class)
76{
77 const unsigned int TBL_LEN =
78 ARRAY_SIZE(iommu_pm_event_class_translation_table);
79 unsigned int i;
80
81 if (event_class < 0)
82 return event_class;
83
84 for (i = 0; i < TBL_LEN; ++i) {
85 if (iommu_pm_event_class_translation_table[i] == event_class)
86 return i;
87 }
88 return MSM_IOMMU_PMU_NO_EVENT_CLASS;
89}
90
91static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
92{
93 /*
94 * IOMMUv0 is in always ON domain so we don't care whether we are
95 * attached or not. We only care whether the PMU is enabled or
96 * not meaning clocks are turned on.
97 */
98 return pmon->enabled;
99}
100
101static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
102{
103 /* No group concept in v0. */
104}
105
106static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
107{
108 /* No group concept in v0. */
109}
110
111static void iommu_pm_set_int_active_high(const struct iommu_info *iommu)
112{
113 unsigned int emmc;
114 emmc = readl_relaxed(iommu->base + EMMC);
115 emmc |= (PM_INT_ACTIVE_HIGH & PM_INT_POL_MASK) << PM_INT_POL_SHIFT;
116 writel_relaxed(emmc, iommu->base + EMMC);
117}
118
119static void iommu_pm_enable(struct iommu_info *iommu)
120{
121 unsigned int emmc;
122 emmc = readl_relaxed(iommu->base + EMMC);
123 emmc |= PM_ENABLE;
124 writel_relaxed(emmc, iommu->base + EMMC);
125}
126
127static void iommu_pm_disable(struct iommu_info *iommu)
128{
129 unsigned int emmc;
130 emmc = readl_relaxed(iommu->base + EMMC);
131 emmc &= ~PM_ENABLE;
132 writel_relaxed(emmc, iommu->base + EMMC);
133}
134
135static void iommu_pm_reset_counters(const struct iommu_info *iommu)
136{
137 unsigned int emmc;
138 emmc = readl_relaxed(iommu->base + EMMC);
139 emmc |= PM_RESET;
140 writel_relaxed(emmc, iommu->base + EMMC);
141}
142
143static void iommu_pm_check_for_overflow(struct iommu_pmon *pmon)
144{
145 struct iommu_pmon_counter *counter;
146 struct iommu_info *iommu = &pmon->iommu;
147 unsigned int reg_value;
148 unsigned int j;
149 struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[0];
150
151 reg_value = readl_relaxed(iommu->base + EMCS);
152 reg_value &= PM_OVFL_FLAG;
153
154 for (j = 0; j < cnt_grp->num_counters; ++j) {
155 counter = &cnt_grp->counters[j];
156
157 if (counter->enabled) {
158 if (reg_value & (1 << counter->absolute_counter_no))
159 counter->overflow_count++;
160 }
161 }
162
163 /* Clear overflow */
164 writel_relaxed(reg_value, iommu->base + EMCS);
165}
166
167static irqreturn_t iommu_pm_evt_ovfl_int_handler(int irq, void *dev_id)
168{
169 struct iommu_pmon *pmon = dev_id;
170 struct iommu_info *iommu = &pmon->iommu;
171
172 mutex_lock(&pmon->lock);
173
174 if (!iommu_pm_is_hw_access_OK(pmon)) {
175 mutex_unlock(&pmon->lock);
176 goto out;
177 }
178
Olav Hauganf75b52e2013-10-01 09:18:03 -0700179 iommu->ops->iommu_lock_acquire(1);
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800180 iommu_pm_check_for_overflow(pmon);
Olav Hauganf75b52e2013-10-01 09:18:03 -0700181 iommu->ops->iommu_lock_release(1);
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800182
183 mutex_unlock(&pmon->lock);
184
185out:
186 return IRQ_HANDLED;
187}
188
189static void iommu_pm_counter_enable(struct iommu_info *iommu,
190 struct iommu_pmon_counter *counter)
191{
192 unsigned int bit_no = counter->absolute_counter_no;
193 unsigned int reg_value;
194
195 /* Clear overflow of counter */
196 reg_value = readl_relaxed(iommu->base + EMCS);
197 reg_value &= (1 << bit_no);
198 writel_relaxed(reg_value, iommu->base + EMCS);
199
200 /* Enable counter */
201 counter->enabled = 1;
202}
203
204static void iommu_pm_counter_disable(struct iommu_info *iommu,
205 struct iommu_pmon_counter *counter)
206{
207 unsigned int bit_no = counter->absolute_counter_no;
208 unsigned int reg_value;
209
210 /* Disable counter */
211 counter->enabled = 0;
212
213 /* Clear overflow of counter */
214 reg_value = readl_relaxed(iommu->base + EMCS);
215 reg_value &= (1 << bit_no);
216 writel_relaxed(reg_value, iommu->base + EMCS);
217}
218
219/*
220 * Must be called after iommu_start_access() is called
221 */
222static void iommu_pm_ovfl_int_enable(struct iommu_info *iommu,
223 const struct iommu_pmon_counter *counter)
224{
225 unsigned int reg_no = counter->absolute_counter_no;
226 unsigned int reg_value;
227
228 /* Enable overflow interrupt for counter */
229 reg_value = readl_relaxed(iommu->base + PMEVTYPER_(reg_no));
230 reg_value |= PM_INT_EN;
231 writel_relaxed(reg_value, iommu->base + PMEVTYPER_(reg_no));
232}
233
234/*
235 * Must be called after iommu_start_access() is called
236 */
237static void iommu_pm_ovfl_int_disable(struct iommu_info *iommu,
238 const struct iommu_pmon_counter *counter)
239{
240 unsigned int reg_no = counter->absolute_counter_no;
241 unsigned int reg_value;
242
243 /* Disable overflow interrupt for counter */
244 reg_value = readl_relaxed(iommu->base + PMEVTYPER_(reg_no));
245 reg_value &= ~PM_INT_EN;
246 writel_relaxed(reg_value, iommu->base + PMEVTYPER_(reg_no));
247}
248
249static void iommu_pm_set_event_class(struct iommu_pmon *pmon,
250 unsigned int count_no,
251 unsigned int event_class)
252{
253 unsigned int reg_no = count_no;
254 unsigned int reg_value;
255 int event = iommu_pm_translate_event_class(event_class);
256
257 if (event == MSM_IOMMU_PMU_NO_EVENT_CLASS)
258 event = 0;
259
260 reg_value = readl_relaxed(pmon->iommu.base + PMEVTYPER_(reg_no));
261 reg_value &= ~(PM_EVENT_TYPE_MASK << PM_EVENT_TYPE_SHIFT);
262 reg_value |= (event & PM_EVENT_TYPE_MASK) << PM_EVENT_TYPE_SHIFT;
263 writel_relaxed(reg_value, pmon->iommu.base + PMEVTYPER_(reg_no));
264}
265
266static unsigned int iommu_pm_read_counter(struct iommu_pmon_counter *counter)
267{
268 struct iommu_pmon *pmon = counter->cnt_group->pmon;
269 struct iommu_info *info = &pmon->iommu;
270 unsigned int cnt_no = counter->absolute_counter_no;
271 return readl_relaxed(info->base + PMEVCNTR_(cnt_no));
272}
273
274static void iommu_pm_initialize_hw(const struct iommu_pmon *pmon)
275{
276 const struct iommu_info *iommu = &pmon->iommu;
277 struct msm_iommu_drvdata *iommu_drvdata =
278 dev_get_drvdata(iommu->iommu_dev);
279
280 /* This is called during bootup device initialization so no need
281 * for locking here.
282 */
283 iommu->ops->iommu_power_on(iommu_drvdata);
Olav Hauganeece7e52013-04-02 10:22:21 -0700284 iommu->ops->iommu_clk_on(iommu_drvdata);
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800285 iommu_pm_set_int_active_high(iommu);
Olav Hauganeece7e52013-04-02 10:22:21 -0700286 iommu->ops->iommu_clk_off(iommu_drvdata);
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800287 iommu->ops->iommu_power_off(iommu_drvdata);
288}
289
290static struct iommu_pm_hw_ops iommu_pm_hw_ops = {
291 .initialize_hw = iommu_pm_initialize_hw,
292 .is_hw_access_OK = iommu_pm_is_hw_access_OK,
293 .grp_enable = iommu_pm_grp_enable,
294 .grp_disable = iommu_pm_grp_disable,
295 .enable_pm = iommu_pm_enable,
296 .disable_pm = iommu_pm_disable,
297 .reset_counters = iommu_pm_reset_counters,
298 .check_for_overflow = iommu_pm_check_for_overflow,
299 .evt_ovfl_int_handler = iommu_pm_evt_ovfl_int_handler,
300 .counter_enable = iommu_pm_counter_enable,
301 .counter_disable = iommu_pm_counter_disable,
302 .ovfl_int_enable = iommu_pm_ovfl_int_enable,
303 .ovfl_int_disable = iommu_pm_ovfl_int_disable,
304 .set_event_class = iommu_pm_set_event_class,
305 .read_counter = iommu_pm_read_counter,
306};
307
308struct iommu_pm_hw_ops *iommu_pm_get_hw_ops_v0(void)
309{
310 return &iommu_pm_hw_ops;
311}
312EXPORT_SYMBOL(iommu_pm_get_hw_ops_v0);
313