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