blob: 2b551847581d5626684deaa3bd060462b46a7381 [file] [log] [blame]
Olav Hauganef69e892013-02-04 13:47:08 -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 IOMMUv1 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 <mach/iommu_hw-v1.h>
22#include <mach/iommu_perfmon.h>
Olav Hauganeece7e52013-04-02 10:22:21 -070023#include <mach/iommu.h>
Olav Hauganef69e892013-02-04 13:47:08 -080024
25#define PMCR_P_MASK (0x1)
26#define PMCR_P_SHIFT (1)
27#define PMCR_P (PMCR_P_MASK << PMCR_P_SHIFT)
28#define PMCFGR_NCG_MASK (0xFF)
29#define PMCFGR_NCG_SHIFT (24)
30#define PMCFGR_NCG (PMCFGR_NCG_MASK << PMCFGR_NCG_SHIFT)
31#define PMCFGR_N_MASK (0xFF)
32#define PMCFGR_N_SHIFT (0)
33#define PMCFGR_N (PMCFGR_N_MASK << PMCFGR_N_SHIFT)
34#define CR_E 0x1
35#define CGCR_CEN 0x800
36#define CGCR_CEN_SHFT (1 << 11)
37#define PMCGCR_CGNC_MASK (0x0F)
38#define PMCGCR_CGNC_SHIFT (24)
39#define PMCGCR_CGNC (PMCGCR_CGNC_MASK << PMCGCR_CGNC_SHIFT)
40#define PMCGCR_(group) (PMCGCR_N + group*4)
41
42#define PMOVSCLR_(n) (PMOVSCLR_N + n*4)
43#define PMCNTENSET_(n) (PMCNTENSET_N + n*4)
44#define PMCNTENCLR_(n) (PMCNTENCLR_N + n*4)
45#define PMINTENSET_(n) (PMINTENSET_N + n*4)
46#define PMINTENCLR_(n) (PMINTENCLR_N + n*4)
47
48#define PMEVCNTR_(n) (PMEVCNTR_N + n*4)
49#define PMEVTYPER_(n) (PMEVTYPER_N + n*4)
50
51
52static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
53{
54 /*
55 * IOMMUv1 is not in the always on domain so we need to make sure
56 * the regulators are turned on in addition to clocks before we allow
57 * access to the hardware thus we check if we have attached to the
58 * IOMMU in addition to checking if we have enabled PMU.
59 */
60 return pmon->enabled && (pmon->iommu_attach_count > 0);
61}
62
63static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
64{
65 unsigned int pmcgcr;
66 pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
67 pmcgcr |= CGCR_CEN;
68 writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
69}
70
71static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
72{
73 unsigned int pmcgcr;
74 pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
75 pmcgcr &= ~CGCR_CEN;
76 writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
77}
78
79static void iommu_pm_enable(struct iommu_info *iommu)
80{
81 unsigned int pmcr;
82 pmcr = readl_relaxed(iommu->base + PMCR);
83 pmcr |= CR_E;
84 writel_relaxed(pmcr, iommu->base + PMCR);
85}
86
87static void iommu_pm_disable(struct iommu_info *iommu)
88{
89 unsigned int pmcr;
90 pmcr = readl_relaxed(iommu->base + PMCR);
91 pmcr &= ~CR_E;
92 writel_relaxed(pmcr, iommu->base + PMCR);
93}
94
95static void iommu_pm_reset_counters(const struct iommu_info *iommu)
96{
97 unsigned int pmcr;
98 pmcr = readl_relaxed(iommu->base + PMCR);
99 pmcr |= PMCR_P;
100 writel_relaxed(pmcr, iommu->base + PMCR);
101}
102
103static void iommu_pm_check_for_overflow(struct iommu_pmon *pmon)
104{
105 struct iommu_pmon_counter *counter;
106 struct iommu_info *iommu = &pmon->iommu;
107 unsigned int reg_no = 0;
108 unsigned int bit_no;
109 unsigned int reg_value;
110 unsigned int i;
111 unsigned int j;
112 unsigned int curr_reg = 0;
113
114 reg_value = readl_relaxed(iommu->base + PMOVSCLR_(curr_reg));
115
116 for (i = 0; i < pmon->num_groups; ++i) {
117 struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
118 for (j = 0; j < cnt_grp->num_counters; ++j) {
119 counter = &cnt_grp->counters[j];
120 reg_no = counter->absolute_counter_no / 32;
121 bit_no = counter->absolute_counter_no % 32;
122 if (reg_no != curr_reg) {
123 /* Clear overflow bits */
124 writel_relaxed(reg_value, iommu->base +
125 PMOVSCLR_(reg_no));
126 curr_reg = reg_no;
127 reg_value = readl_relaxed(iommu->base +
128 PMOVSCLR_(curr_reg));
129 }
130
131 if (counter->enabled) {
132 if (reg_value & (1 << bit_no))
133 counter->overflow_count++;
134 }
135 }
136 }
137
138 /* Clear overflow */
139 writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
140}
141
142static irqreturn_t iommu_pm_evt_ovfl_int_handler(int irq, void *dev_id)
143{
144 struct iommu_pmon *pmon = dev_id;
145 struct iommu_info *iommu = &pmon->iommu;
146
147 mutex_lock(&pmon->lock);
148
149 if (!iommu_pm_is_hw_access_OK(pmon)) {
150 mutex_unlock(&pmon->lock);
151 goto out;
152 }
153
Olav Hauganf75b52e2013-10-01 09:18:03 -0700154 iommu->ops->iommu_lock_acquire(0);
Olav Hauganef69e892013-02-04 13:47:08 -0800155 iommu_pm_check_for_overflow(pmon);
Olav Hauganf75b52e2013-10-01 09:18:03 -0700156 iommu->ops->iommu_lock_release(0);
Olav Hauganef69e892013-02-04 13:47:08 -0800157
158 mutex_unlock(&pmon->lock);
159
160out:
161 return IRQ_HANDLED;
162}
163
164static void iommu_pm_counter_enable(struct iommu_info *iommu,
165 struct iommu_pmon_counter *counter)
166{
167 unsigned int reg_no = counter->absolute_counter_no / 32;
168 unsigned int bit_no = counter->absolute_counter_no % 32;
169 unsigned int reg_value;
170
171 /* Clear overflow of counter */
172 reg_value = 1 << bit_no;
173 writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
174
175 /* Enable counter */
176 writel_relaxed(reg_value, iommu->base + PMCNTENSET_(reg_no));
177 counter->enabled = 1;
178}
179
180static void iommu_pm_counter_disable(struct iommu_info *iommu,
181 struct iommu_pmon_counter *counter)
182{
183 unsigned int reg_no = counter->absolute_counter_no / 32;
184 unsigned int bit_no = counter->absolute_counter_no % 32;
185 unsigned int reg_value;
186
187 counter->enabled = 0;
188
189 /* Disable counter */
190 reg_value = 1 << bit_no;
191 writel_relaxed(reg_value, iommu->base + PMCNTENCLR_(reg_no));
192
193 /* Clear overflow of counter */
194 writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
195}
196
197/*
198 * Must be called after iommu_start_access() is called
199 */
200static void iommu_pm_ovfl_int_enable(struct iommu_info *iommu,
201 const struct iommu_pmon_counter *counter)
202{
203 unsigned int reg_no = counter->absolute_counter_no / 32;
204 unsigned int bit_no = counter->absolute_counter_no % 32;
205 unsigned int reg_value;
206
207 /* Enable overflow interrupt for counter */
208 reg_value = (1 << bit_no);
209 writel_relaxed(reg_value, iommu->base + PMINTENSET_(reg_no));
210}
211
212/*
213 * Must be called after iommu_start_access() is called
214 */
215static void iommu_pm_ovfl_int_disable(struct iommu_info *iommu,
216 const struct iommu_pmon_counter *counter)
217{
218 unsigned int reg_no = counter->absolute_counter_no / 32;
219 unsigned int bit_no = counter->absolute_counter_no % 32;
220 unsigned int reg_value;
221
222 /* Disable overflow interrupt for counter */
223 reg_value = 1 << bit_no;
224 writel_relaxed(reg_value, iommu->base + PMINTENCLR_(reg_no));
225}
226
227static void iommu_pm_set_event_class(struct iommu_pmon *pmon,
228 unsigned int count_no,
229 unsigned int event_class)
230{
231 writel_relaxed(event_class, pmon->iommu.base + PMEVTYPER_(count_no));
232}
233
234static unsigned int iommu_pm_read_counter(struct iommu_pmon_counter *counter)
235{
236 struct iommu_pmon *pmon = counter->cnt_group->pmon;
237 struct iommu_info *info = &pmon->iommu;
238 unsigned int cnt_no = counter->absolute_counter_no;
239 return readl_relaxed(info->base + PMEVCNTR_(cnt_no));
240}
241
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800242static void iommu_pm_initialize_hw(const struct iommu_pmon *pmon)
243{
244 /* No initialization needed */
245}
246
Olav Hauganef69e892013-02-04 13:47:08 -0800247static struct iommu_pm_hw_ops iommu_pm_hw_ops = {
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800248 .initialize_hw = iommu_pm_initialize_hw,
Olav Hauganef69e892013-02-04 13:47:08 -0800249 .is_hw_access_OK = iommu_pm_is_hw_access_OK,
250 .grp_enable = iommu_pm_grp_enable,
251 .grp_disable = iommu_pm_grp_disable,
252 .enable_pm = iommu_pm_enable,
253 .disable_pm = iommu_pm_disable,
254 .reset_counters = iommu_pm_reset_counters,
255 .check_for_overflow = iommu_pm_check_for_overflow,
256 .evt_ovfl_int_handler = iommu_pm_evt_ovfl_int_handler,
257 .counter_enable = iommu_pm_counter_enable,
258 .counter_disable = iommu_pm_counter_disable,
259 .ovfl_int_enable = iommu_pm_ovfl_int_enable,
260 .ovfl_int_disable = iommu_pm_ovfl_int_disable,
261 .set_event_class = iommu_pm_set_event_class,
262 .read_counter = iommu_pm_read_counter,
263};
264
265struct iommu_pm_hw_ops *iommu_pm_get_hw_ops_v1(void)
266{
267 return &iommu_pm_hw_ops;
268}
269EXPORT_SYMBOL(iommu_pm_get_hw_ops_v1);
270