Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/arm/common/vic.c
10817 views
1
/*
2
* linux/arch/arm/common/vic.c
3
*
4
* Copyright (C) 1999 - 2003 ARM Limited
5
* Copyright (C) 2000 Deep Blue Solutions Ltd
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 as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
*/
21
22
#include <linux/init.h>
23
#include <linux/list.h>
24
#include <linux/io.h>
25
#include <linux/syscore_ops.h>
26
#include <linux/device.h>
27
#include <linux/amba/bus.h>
28
29
#include <asm/mach/irq.h>
30
#include <asm/hardware/vic.h>
31
32
#ifdef CONFIG_PM
33
/**
34
* struct vic_device - VIC PM device
35
* @irq: The IRQ number for the base of the VIC.
36
* @base: The register base for the VIC.
37
* @resume_sources: A bitmask of interrupts for resume.
38
* @resume_irqs: The IRQs enabled for resume.
39
* @int_select: Save for VIC_INT_SELECT.
40
* @int_enable: Save for VIC_INT_ENABLE.
41
* @soft_int: Save for VIC_INT_SOFT.
42
* @protect: Save for VIC_PROTECT.
43
*/
44
struct vic_device {
45
void __iomem *base;
46
int irq;
47
u32 resume_sources;
48
u32 resume_irqs;
49
u32 int_select;
50
u32 int_enable;
51
u32 soft_int;
52
u32 protect;
53
};
54
55
/* we cannot allocate memory when VICs are initially registered */
56
static struct vic_device vic_devices[CONFIG_ARM_VIC_NR];
57
58
static int vic_id;
59
#endif /* CONFIG_PM */
60
61
/**
62
* vic_init2 - common initialisation code
63
* @base: Base of the VIC.
64
*
65
* Common initialisation code for registration
66
* and resume.
67
*/
68
static void vic_init2(void __iomem *base)
69
{
70
int i;
71
72
for (i = 0; i < 16; i++) {
73
void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
74
writel(VIC_VECT_CNTL_ENABLE | i, reg);
75
}
76
77
writel(32, base + VIC_PL190_DEF_VECT_ADDR);
78
}
79
80
#ifdef CONFIG_PM
81
static void resume_one_vic(struct vic_device *vic)
82
{
83
void __iomem *base = vic->base;
84
85
printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base);
86
87
/* re-initialise static settings */
88
vic_init2(base);
89
90
writel(vic->int_select, base + VIC_INT_SELECT);
91
writel(vic->protect, base + VIC_PROTECT);
92
93
/* set the enabled ints and then clear the non-enabled */
94
writel(vic->int_enable, base + VIC_INT_ENABLE);
95
writel(~vic->int_enable, base + VIC_INT_ENABLE_CLEAR);
96
97
/* and the same for the soft-int register */
98
99
writel(vic->soft_int, base + VIC_INT_SOFT);
100
writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR);
101
}
102
103
static void vic_resume(void)
104
{
105
int id;
106
107
for (id = vic_id - 1; id >= 0; id--)
108
resume_one_vic(vic_devices + id);
109
}
110
111
static void suspend_one_vic(struct vic_device *vic)
112
{
113
void __iomem *base = vic->base;
114
115
printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base);
116
117
vic->int_select = readl(base + VIC_INT_SELECT);
118
vic->int_enable = readl(base + VIC_INT_ENABLE);
119
vic->soft_int = readl(base + VIC_INT_SOFT);
120
vic->protect = readl(base + VIC_PROTECT);
121
122
/* set the interrupts (if any) that are used for
123
* resuming the system */
124
125
writel(vic->resume_irqs, base + VIC_INT_ENABLE);
126
writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR);
127
}
128
129
static int vic_suspend(void)
130
{
131
int id;
132
133
for (id = 0; id < vic_id; id++)
134
suspend_one_vic(vic_devices + id);
135
136
return 0;
137
}
138
139
struct syscore_ops vic_syscore_ops = {
140
.suspend = vic_suspend,
141
.resume = vic_resume,
142
};
143
144
/**
145
* vic_pm_init - initicall to register VIC pm
146
*
147
* This is called via late_initcall() to register
148
* the resources for the VICs due to the early
149
* nature of the VIC's registration.
150
*/
151
static int __init vic_pm_init(void)
152
{
153
if (vic_id > 0)
154
register_syscore_ops(&vic_syscore_ops);
155
156
return 0;
157
}
158
late_initcall(vic_pm_init);
159
160
/**
161
* vic_pm_register - Register a VIC for later power management control
162
* @base: The base address of the VIC.
163
* @irq: The base IRQ for the VIC.
164
* @resume_sources: bitmask of interrupts allowed for resume sources.
165
*
166
* Register the VIC with the system device tree so that it can be notified
167
* of suspend and resume requests and ensure that the correct actions are
168
* taken to re-instate the settings on resume.
169
*/
170
static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources)
171
{
172
struct vic_device *v;
173
174
if (vic_id >= ARRAY_SIZE(vic_devices))
175
printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
176
else {
177
v = &vic_devices[vic_id];
178
v->base = base;
179
v->resume_sources = resume_sources;
180
v->irq = irq;
181
vic_id++;
182
}
183
}
184
#else
185
static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { }
186
#endif /* CONFIG_PM */
187
188
static void vic_ack_irq(struct irq_data *d)
189
{
190
void __iomem *base = irq_data_get_irq_chip_data(d);
191
unsigned int irq = d->irq & 31;
192
writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
193
/* moreover, clear the soft-triggered, in case it was the reason */
194
writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
195
}
196
197
static void vic_mask_irq(struct irq_data *d)
198
{
199
void __iomem *base = irq_data_get_irq_chip_data(d);
200
unsigned int irq = d->irq & 31;
201
writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
202
}
203
204
static void vic_unmask_irq(struct irq_data *d)
205
{
206
void __iomem *base = irq_data_get_irq_chip_data(d);
207
unsigned int irq = d->irq & 31;
208
writel(1 << irq, base + VIC_INT_ENABLE);
209
}
210
211
#if defined(CONFIG_PM)
212
static struct vic_device *vic_from_irq(unsigned int irq)
213
{
214
struct vic_device *v = vic_devices;
215
unsigned int base_irq = irq & ~31;
216
int id;
217
218
for (id = 0; id < vic_id; id++, v++) {
219
if (v->irq == base_irq)
220
return v;
221
}
222
223
return NULL;
224
}
225
226
static int vic_set_wake(struct irq_data *d, unsigned int on)
227
{
228
struct vic_device *v = vic_from_irq(d->irq);
229
unsigned int off = d->irq & 31;
230
u32 bit = 1 << off;
231
232
if (!v)
233
return -EINVAL;
234
235
if (!(bit & v->resume_sources))
236
return -EINVAL;
237
238
if (on)
239
v->resume_irqs |= bit;
240
else
241
v->resume_irqs &= ~bit;
242
243
return 0;
244
}
245
#else
246
#define vic_set_wake NULL
247
#endif /* CONFIG_PM */
248
249
static struct irq_chip vic_chip = {
250
.name = "VIC",
251
.irq_ack = vic_ack_irq,
252
.irq_mask = vic_mask_irq,
253
.irq_unmask = vic_unmask_irq,
254
.irq_set_wake = vic_set_wake,
255
};
256
257
static void __init vic_disable(void __iomem *base)
258
{
259
writel(0, base + VIC_INT_SELECT);
260
writel(0, base + VIC_INT_ENABLE);
261
writel(~0, base + VIC_INT_ENABLE_CLEAR);
262
writel(0, base + VIC_IRQ_STATUS);
263
writel(0, base + VIC_ITCR);
264
writel(~0, base + VIC_INT_SOFT_CLEAR);
265
}
266
267
static void __init vic_clear_interrupts(void __iomem *base)
268
{
269
unsigned int i;
270
271
writel(0, base + VIC_PL190_VECT_ADDR);
272
for (i = 0; i < 19; i++) {
273
unsigned int value;
274
275
value = readl(base + VIC_PL190_VECT_ADDR);
276
writel(value, base + VIC_PL190_VECT_ADDR);
277
}
278
}
279
280
static void __init vic_set_irq_sources(void __iomem *base,
281
unsigned int irq_start, u32 vic_sources)
282
{
283
unsigned int i;
284
285
for (i = 0; i < 32; i++) {
286
if (vic_sources & (1 << i)) {
287
unsigned int irq = irq_start + i;
288
289
irq_set_chip_and_handler(irq, &vic_chip,
290
handle_level_irq);
291
irq_set_chip_data(irq, base);
292
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
293
}
294
}
295
}
296
297
/*
298
* The PL190 cell from ARM has been modified by ST to handle 64 interrupts.
299
* The original cell has 32 interrupts, while the modified one has 64,
300
* replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case
301
* the probe function is called twice, with base set to offset 000
302
* and 020 within the page. We call this "second block".
303
*/
304
static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
305
u32 vic_sources)
306
{
307
unsigned int i;
308
int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0;
309
310
/* Disable all interrupts initially. */
311
vic_disable(base);
312
313
/*
314
* Make sure we clear all existing interrupts. The vector registers
315
* in this cell are after the second block of general registers,
316
* so we can address them using standard offsets, but only from
317
* the second base address, which is 0x20 in the page
318
*/
319
if (vic_2nd_block) {
320
vic_clear_interrupts(base);
321
322
/* ST has 16 vectors as well, but we don't enable them by now */
323
for (i = 0; i < 16; i++) {
324
void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
325
writel(0, reg);
326
}
327
328
writel(32, base + VIC_PL190_DEF_VECT_ADDR);
329
}
330
331
vic_set_irq_sources(base, irq_start, vic_sources);
332
}
333
334
/**
335
* vic_init - initialise a vectored interrupt controller
336
* @base: iomem base address
337
* @irq_start: starting interrupt number, must be muliple of 32
338
* @vic_sources: bitmask of interrupt sources to allow
339
* @resume_sources: bitmask of interrupt sources to allow for resume
340
*/
341
void __init vic_init(void __iomem *base, unsigned int irq_start,
342
u32 vic_sources, u32 resume_sources)
343
{
344
unsigned int i;
345
u32 cellid = 0;
346
enum amba_vendor vendor;
347
348
/* Identify which VIC cell this one is, by reading the ID */
349
for (i = 0; i < 4; i++) {
350
u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
351
cellid |= (readl(addr) & 0xff) << (8 * i);
352
}
353
vendor = (cellid >> 12) & 0xff;
354
printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
355
base, cellid, vendor);
356
357
switch(vendor) {
358
case AMBA_VENDOR_ST:
359
vic_init_st(base, irq_start, vic_sources);
360
return;
361
default:
362
printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
363
/* fall through */
364
case AMBA_VENDOR_ARM:
365
break;
366
}
367
368
/* Disable all interrupts initially. */
369
vic_disable(base);
370
371
/* Make sure we clear all existing interrupts */
372
vic_clear_interrupts(base);
373
374
vic_init2(base);
375
376
vic_set_irq_sources(base, irq_start, vic_sources);
377
378
vic_pm_register(base, irq_start, resume_sources);
379
}
380
381