Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/bcm63xx/irq.c
10817 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* Copyright (C) 2008 Maxime Bizon <[email protected]>
7
* Copyright (C) 2008 Nicolas Schichan <[email protected]>
8
*/
9
10
#include <linux/kernel.h>
11
#include <linux/init.h>
12
#include <linux/interrupt.h>
13
#include <linux/module.h>
14
#include <linux/irq.h>
15
#include <asm/irq_cpu.h>
16
#include <asm/mipsregs.h>
17
#include <bcm63xx_cpu.h>
18
#include <bcm63xx_regs.h>
19
#include <bcm63xx_io.h>
20
#include <bcm63xx_irq.h>
21
22
/*
23
* dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
24
* prioritize any interrupt relatively to another. the static counter
25
* will resume the loop where it ended the last time we left this
26
* function.
27
*/
28
static void bcm63xx_irq_dispatch_internal(void)
29
{
30
u32 pending;
31
static int i;
32
33
pending = bcm_perf_readl(PERF_IRQMASK_REG) &
34
bcm_perf_readl(PERF_IRQSTAT_REG);
35
36
if (!pending)
37
return ;
38
39
while (1) {
40
int to_call = i;
41
42
i = (i + 1) & 0x1f;
43
if (pending & (1 << to_call)) {
44
do_IRQ(to_call + IRQ_INTERNAL_BASE);
45
break;
46
}
47
}
48
}
49
50
asmlinkage void plat_irq_dispatch(void)
51
{
52
u32 cause;
53
54
do {
55
cause = read_c0_cause() & read_c0_status() & ST0_IM;
56
57
if (!cause)
58
break;
59
60
if (cause & CAUSEF_IP7)
61
do_IRQ(7);
62
if (cause & CAUSEF_IP2)
63
bcm63xx_irq_dispatch_internal();
64
if (cause & CAUSEF_IP3)
65
do_IRQ(IRQ_EXT_0);
66
if (cause & CAUSEF_IP4)
67
do_IRQ(IRQ_EXT_1);
68
if (cause & CAUSEF_IP5)
69
do_IRQ(IRQ_EXT_2);
70
if (cause & CAUSEF_IP6)
71
do_IRQ(IRQ_EXT_3);
72
} while (1);
73
}
74
75
/*
76
* internal IRQs operations: only mask/unmask on PERF irq mask
77
* register.
78
*/
79
static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
80
{
81
unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
82
u32 mask;
83
84
mask = bcm_perf_readl(PERF_IRQMASK_REG);
85
mask &= ~(1 << irq);
86
bcm_perf_writel(mask, PERF_IRQMASK_REG);
87
}
88
89
static void bcm63xx_internal_irq_unmask(struct irq_data *d)
90
{
91
unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
92
u32 mask;
93
94
mask = bcm_perf_readl(PERF_IRQMASK_REG);
95
mask |= (1 << irq);
96
bcm_perf_writel(mask, PERF_IRQMASK_REG);
97
}
98
99
/*
100
* external IRQs operations: mask/unmask and clear on PERF external
101
* irq control register.
102
*/
103
static void bcm63xx_external_irq_mask(struct irq_data *d)
104
{
105
unsigned int irq = d->irq - IRQ_EXT_BASE;
106
u32 reg;
107
108
reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
109
reg &= ~EXTIRQ_CFG_MASK(irq);
110
bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
111
}
112
113
static void bcm63xx_external_irq_unmask(struct irq_data *d)
114
{
115
unsigned int irq = d->irq - IRQ_EXT_BASE;
116
u32 reg;
117
118
reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
119
reg |= EXTIRQ_CFG_MASK(irq);
120
bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
121
}
122
123
static void bcm63xx_external_irq_clear(struct irq_data *d)
124
{
125
unsigned int irq = d->irq - IRQ_EXT_BASE;
126
u32 reg;
127
128
reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
129
reg |= EXTIRQ_CFG_CLEAR(irq);
130
bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
131
}
132
133
static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
134
{
135
set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
136
irq_enable_hazard();
137
bcm63xx_external_irq_unmask(d);
138
return 0;
139
}
140
141
static void bcm63xx_external_irq_shutdown(struct irq_data *d)
142
{
143
bcm63xx_external_irq_mask(d);
144
clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
145
irq_disable_hazard();
146
}
147
148
static int bcm63xx_external_irq_set_type(struct irq_data *d,
149
unsigned int flow_type)
150
{
151
unsigned int irq = d->irq - IRQ_EXT_BASE;
152
u32 reg;
153
154
flow_type &= IRQ_TYPE_SENSE_MASK;
155
156
if (flow_type == IRQ_TYPE_NONE)
157
flow_type = IRQ_TYPE_LEVEL_LOW;
158
159
reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
160
switch (flow_type) {
161
case IRQ_TYPE_EDGE_BOTH:
162
reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
163
reg |= EXTIRQ_CFG_BOTHEDGE(irq);
164
break;
165
166
case IRQ_TYPE_EDGE_RISING:
167
reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
168
reg |= EXTIRQ_CFG_SENSE(irq);
169
reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
170
break;
171
172
case IRQ_TYPE_EDGE_FALLING:
173
reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
174
reg &= ~EXTIRQ_CFG_SENSE(irq);
175
reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
176
break;
177
178
case IRQ_TYPE_LEVEL_HIGH:
179
reg |= EXTIRQ_CFG_LEVELSENSE(irq);
180
reg |= EXTIRQ_CFG_SENSE(irq);
181
break;
182
183
case IRQ_TYPE_LEVEL_LOW:
184
reg |= EXTIRQ_CFG_LEVELSENSE(irq);
185
reg &= ~EXTIRQ_CFG_SENSE(irq);
186
break;
187
188
default:
189
printk(KERN_ERR "bogus flow type combination given !\n");
190
return -EINVAL;
191
}
192
bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
193
194
irqd_set_trigger_type(d, flow_type);
195
if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
196
__irq_set_handler_locked(d->irq, handle_level_irq);
197
else
198
__irq_set_handler_locked(d->irq, handle_edge_irq);
199
200
return IRQ_SET_MASK_OK_NOCOPY;
201
}
202
203
static struct irq_chip bcm63xx_internal_irq_chip = {
204
.name = "bcm63xx_ipic",
205
.irq_mask = bcm63xx_internal_irq_mask,
206
.irq_unmask = bcm63xx_internal_irq_unmask,
207
};
208
209
static struct irq_chip bcm63xx_external_irq_chip = {
210
.name = "bcm63xx_epic",
211
.irq_startup = bcm63xx_external_irq_startup,
212
.irq_shutdown = bcm63xx_external_irq_shutdown,
213
214
.irq_ack = bcm63xx_external_irq_clear,
215
216
.irq_mask = bcm63xx_external_irq_mask,
217
.irq_unmask = bcm63xx_external_irq_unmask,
218
219
.irq_set_type = bcm63xx_external_irq_set_type,
220
};
221
222
static struct irqaction cpu_ip2_cascade_action = {
223
.handler = no_action,
224
.name = "cascade_ip2",
225
};
226
227
void __init arch_init_irq(void)
228
{
229
int i;
230
231
mips_cpu_irq_init();
232
for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
233
irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
234
handle_level_irq);
235
236
for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
237
irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
238
handle_edge_irq);
239
240
setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
241
}
242
243