Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/avr32/mach-at32ap/intc.c
10817 views
1
/*
2
* Copyright (C) 2006, 2008 Atmel Corporation
3
*
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 2 as
6
* published by the Free Software Foundation.
7
*/
8
9
#include <linux/clk.h>
10
#include <linux/err.h>
11
#include <linux/init.h>
12
#include <linux/interrupt.h>
13
#include <linux/irq.h>
14
#include <linux/platform_device.h>
15
#include <linux/syscore_ops.h>
16
17
#include <asm/io.h>
18
19
#include "intc.h"
20
21
struct intc {
22
void __iomem *regs;
23
struct irq_chip chip;
24
#ifdef CONFIG_PM
25
unsigned long suspend_ipr;
26
unsigned long saved_ipr[64];
27
#endif
28
};
29
30
extern struct platform_device at32_intc0_device;
31
32
/*
33
* TODO: We may be able to implement mask/unmask by setting IxM flags
34
* in the status register.
35
*/
36
static void intc_mask_irq(struct irq_data *d)
37
{
38
39
}
40
41
static void intc_unmask_irq(struct irq_data *d)
42
{
43
44
}
45
46
static struct intc intc0 = {
47
.chip = {
48
.name = "intc",
49
.irq_mask = intc_mask_irq,
50
.irq_unmask = intc_unmask_irq,
51
},
52
};
53
54
/*
55
* All interrupts go via intc at some point.
56
*/
57
asmlinkage void do_IRQ(int level, struct pt_regs *regs)
58
{
59
struct pt_regs *old_regs;
60
unsigned int irq;
61
unsigned long status_reg;
62
63
local_irq_disable();
64
65
old_regs = set_irq_regs(regs);
66
67
irq_enter();
68
69
irq = intc_readl(&intc0, INTCAUSE0 - 4 * level);
70
generic_handle_irq(irq);
71
72
/*
73
* Clear all interrupt level masks so that we may handle
74
* interrupts during softirq processing. If this is a nested
75
* interrupt, interrupts must stay globally disabled until we
76
* return.
77
*/
78
status_reg = sysreg_read(SR);
79
status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M)
80
| SYSREG_BIT(I2M) | SYSREG_BIT(I3M));
81
sysreg_write(SR, status_reg);
82
83
irq_exit();
84
85
set_irq_regs(old_regs);
86
}
87
88
void __init init_IRQ(void)
89
{
90
extern void _evba(void);
91
extern void irq_level0(void);
92
struct resource *regs;
93
struct clk *pclk;
94
unsigned int i;
95
u32 offset, readback;
96
97
regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0);
98
if (!regs) {
99
printk(KERN_EMERG "intc: no mmio resource defined\n");
100
goto fail;
101
}
102
pclk = clk_get(&at32_intc0_device.dev, "pclk");
103
if (IS_ERR(pclk)) {
104
printk(KERN_EMERG "intc: no clock defined\n");
105
goto fail;
106
}
107
108
clk_enable(pclk);
109
110
intc0.regs = ioremap(regs->start, regs->end - regs->start + 1);
111
if (!intc0.regs) {
112
printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n",
113
(unsigned long)regs->start);
114
goto fail;
115
}
116
117
/*
118
* Initialize all interrupts to level 0 (lowest priority). The
119
* priority level may be changed by calling
120
* irq_set_priority().
121
*
122
*/
123
offset = (unsigned long)&irq_level0 - (unsigned long)&_evba;
124
for (i = 0; i < NR_INTERNAL_IRQS; i++) {
125
intc_writel(&intc0, INTPR0 + 4 * i, offset);
126
readback = intc_readl(&intc0, INTPR0 + 4 * i);
127
if (readback == offset)
128
irq_set_chip_and_handler(i, &intc0.chip,
129
handle_simple_irq);
130
}
131
132
/* Unmask all interrupt levels */
133
sysreg_write(SR, (sysreg_read(SR)
134
& ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M)));
135
136
return;
137
138
fail:
139
panic("Interrupt controller initialization failed!\n");
140
}
141
142
#ifdef CONFIG_PM
143
void intc_set_suspend_handler(unsigned long offset)
144
{
145
intc0.suspend_ipr = offset;
146
}
147
148
static int intc_suspend(void)
149
{
150
int i;
151
152
if (unlikely(!irqs_disabled())) {
153
pr_err("intc_suspend: called with interrupts enabled\n");
154
return -EINVAL;
155
}
156
157
if (unlikely(!intc0.suspend_ipr)) {
158
pr_err("intc_suspend: suspend_ipr not initialized\n");
159
return -EINVAL;
160
}
161
162
for (i = 0; i < 64; i++) {
163
intc0.saved_ipr[i] = intc_readl(&intc0, INTPR0 + 4 * i);
164
intc_writel(&intc0, INTPR0 + 4 * i, intc0.suspend_ipr);
165
}
166
167
return 0;
168
}
169
170
static void intc_resume(void)
171
{
172
int i;
173
174
for (i = 0; i < 64; i++)
175
intc_writel(&intc0, INTPR0 + 4 * i, intc0.saved_ipr[i]);
176
}
177
#else
178
#define intc_suspend NULL
179
#define intc_resume NULL
180
#endif
181
182
static struct syscore_ops intc_syscore_ops = {
183
.suspend = intc_suspend,
184
.resume = intc_resume,
185
};
186
187
static int __init intc_init_syscore(void)
188
{
189
register_syscore_ops(&intc_syscore_ops);
190
191
return 0;
192
}
193
device_initcall(intc_init_syscore);
194
195
unsigned long intc_get_pending(unsigned int group)
196
{
197
return intc_readl(&intc0, INTREQ0 + 4 * group);
198
}
199
EXPORT_SYMBOL_GPL(intc_get_pending);
200
201