Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2008 Ilya Yanok, Emcraft Systems
4
*/
5
6
#include <linux/irq.h>
7
#include <linux/of_address.h>
8
#include <linux/of_irq.h>
9
#include <linux/io.h>
10
11
#include "socrates_fpga_pic.h"
12
13
/*
14
* The FPGA supports 9 interrupt sources, which can be routed to 3
15
* interrupt request lines of the MPIC. The line to be used can be
16
* specified through the third cell of FDT property "interrupts".
17
*/
18
19
#define SOCRATES_FPGA_NUM_IRQS 9
20
21
#define FPGA_PIC_IRQCFG (0x0)
22
#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
23
24
#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
25
26
struct socrates_fpga_irq_info {
27
unsigned int irq_line;
28
int type;
29
};
30
31
/*
32
* Interrupt routing and type table
33
*
34
* IRQ_TYPE_NONE means the interrupt type is configurable,
35
* otherwise it's fixed to the specified value.
36
*/
37
static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
38
[0] = {0, IRQ_TYPE_NONE},
39
[1] = {0, IRQ_TYPE_LEVEL_HIGH},
40
[2] = {0, IRQ_TYPE_LEVEL_LOW},
41
[3] = {0, IRQ_TYPE_NONE},
42
[4] = {0, IRQ_TYPE_NONE},
43
[5] = {0, IRQ_TYPE_NONE},
44
[6] = {0, IRQ_TYPE_NONE},
45
[7] = {0, IRQ_TYPE_NONE},
46
[8] = {0, IRQ_TYPE_LEVEL_HIGH},
47
};
48
49
static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
50
51
static void __iomem *socrates_fpga_pic_iobase;
52
static struct irq_domain *socrates_fpga_pic_irq_host;
53
static unsigned int socrates_fpga_irqs[3];
54
55
static inline uint32_t socrates_fpga_pic_read(int reg)
56
{
57
return in_be32(socrates_fpga_pic_iobase + reg);
58
}
59
60
static inline void socrates_fpga_pic_write(int reg, uint32_t val)
61
{
62
out_be32(socrates_fpga_pic_iobase + reg, val);
63
}
64
65
static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
66
{
67
uint32_t cause;
68
unsigned long flags;
69
int i;
70
71
/* Check irq line routed to the MPIC */
72
for (i = 0; i < 3; i++) {
73
if (irq == socrates_fpga_irqs[i])
74
break;
75
}
76
if (i == 3)
77
return 0;
78
79
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
80
cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
81
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
82
for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
83
if (cause >> (i + 16))
84
break;
85
}
86
return irq_find_mapping(socrates_fpga_pic_irq_host,
87
(irq_hw_number_t)i);
88
}
89
90
static void socrates_fpga_pic_cascade(struct irq_desc *desc)
91
{
92
struct irq_chip *chip = irq_desc_get_chip(desc);
93
unsigned int irq = irq_desc_get_irq(desc);
94
unsigned int cascade_irq;
95
96
/*
97
* See if we actually have an interrupt, call generic handling code if
98
* we do.
99
*/
100
cascade_irq = socrates_fpga_pic_get_irq(irq);
101
102
if (cascade_irq)
103
generic_handle_irq(cascade_irq);
104
chip->irq_eoi(&desc->irq_data);
105
}
106
107
static void socrates_fpga_pic_ack(struct irq_data *d)
108
{
109
unsigned long flags;
110
unsigned int irq_line, hwirq = irqd_to_hwirq(d);
111
uint32_t mask;
112
113
irq_line = fpga_irqs[hwirq].irq_line;
114
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
115
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
116
& SOCRATES_FPGA_IRQ_MASK;
117
mask |= (1 << (hwirq + 16));
118
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
119
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
120
}
121
122
static void socrates_fpga_pic_mask(struct irq_data *d)
123
{
124
unsigned long flags;
125
unsigned int hwirq = irqd_to_hwirq(d);
126
int irq_line;
127
u32 mask;
128
129
irq_line = fpga_irqs[hwirq].irq_line;
130
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
131
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
132
& SOCRATES_FPGA_IRQ_MASK;
133
mask &= ~(1 << hwirq);
134
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
135
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
136
}
137
138
static void socrates_fpga_pic_mask_ack(struct irq_data *d)
139
{
140
unsigned long flags;
141
unsigned int hwirq = irqd_to_hwirq(d);
142
int irq_line;
143
u32 mask;
144
145
irq_line = fpga_irqs[hwirq].irq_line;
146
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
147
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
148
& SOCRATES_FPGA_IRQ_MASK;
149
mask &= ~(1 << hwirq);
150
mask |= (1 << (hwirq + 16));
151
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
152
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
153
}
154
155
static void socrates_fpga_pic_unmask(struct irq_data *d)
156
{
157
unsigned long flags;
158
unsigned int hwirq = irqd_to_hwirq(d);
159
int irq_line;
160
u32 mask;
161
162
irq_line = fpga_irqs[hwirq].irq_line;
163
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
164
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
165
& SOCRATES_FPGA_IRQ_MASK;
166
mask |= (1 << hwirq);
167
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
168
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
169
}
170
171
static void socrates_fpga_pic_eoi(struct irq_data *d)
172
{
173
unsigned long flags;
174
unsigned int hwirq = irqd_to_hwirq(d);
175
int irq_line;
176
u32 mask;
177
178
irq_line = fpga_irqs[hwirq].irq_line;
179
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
180
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
181
& SOCRATES_FPGA_IRQ_MASK;
182
mask |= (1 << (hwirq + 16));
183
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
184
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
185
}
186
187
static int socrates_fpga_pic_set_type(struct irq_data *d,
188
unsigned int flow_type)
189
{
190
unsigned long flags;
191
unsigned int hwirq = irqd_to_hwirq(d);
192
int polarity;
193
u32 mask;
194
195
if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
196
return -EINVAL;
197
198
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
199
case IRQ_TYPE_LEVEL_HIGH:
200
polarity = 1;
201
break;
202
case IRQ_TYPE_LEVEL_LOW:
203
polarity = 0;
204
break;
205
default:
206
return -EINVAL;
207
}
208
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
209
mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
210
if (polarity)
211
mask |= (1 << hwirq);
212
else
213
mask &= ~(1 << hwirq);
214
socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
215
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
216
return 0;
217
}
218
219
static struct irq_chip socrates_fpga_pic_chip = {
220
.name = "FPGA-PIC",
221
.irq_ack = socrates_fpga_pic_ack,
222
.irq_mask = socrates_fpga_pic_mask,
223
.irq_mask_ack = socrates_fpga_pic_mask_ack,
224
.irq_unmask = socrates_fpga_pic_unmask,
225
.irq_eoi = socrates_fpga_pic_eoi,
226
.irq_set_type = socrates_fpga_pic_set_type,
227
};
228
229
static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
230
irq_hw_number_t hwirq)
231
{
232
/* All interrupts are LEVEL sensitive */
233
irq_set_status_flags(virq, IRQ_LEVEL);
234
irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
235
handle_fasteoi_irq);
236
237
return 0;
238
}
239
240
static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
241
struct device_node *ct, const u32 *intspec, unsigned int intsize,
242
irq_hw_number_t *out_hwirq, unsigned int *out_flags)
243
{
244
struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
245
246
*out_hwirq = intspec[0];
247
if (fpga_irq->type == IRQ_TYPE_NONE) {
248
/* type is configurable */
249
if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
250
intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
251
pr_warn("FPGA PIC: invalid irq type, setting default active low\n");
252
*out_flags = IRQ_TYPE_LEVEL_LOW;
253
} else {
254
*out_flags = intspec[1];
255
}
256
} else {
257
/* type is fixed */
258
*out_flags = fpga_irq->type;
259
}
260
261
/* Use specified interrupt routing */
262
if (intspec[2] <= 2)
263
fpga_irq->irq_line = intspec[2];
264
else
265
pr_warn("FPGA PIC: invalid irq routing\n");
266
267
return 0;
268
}
269
270
static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
271
.map = socrates_fpga_pic_host_map,
272
.xlate = socrates_fpga_pic_host_xlate,
273
};
274
275
void __init socrates_fpga_pic_init(struct device_node *pic)
276
{
277
unsigned long flags;
278
int i;
279
280
/* Setup an irq_domain structure */
281
socrates_fpga_pic_irq_host = irq_domain_create_linear(of_fwnode_handle(pic),
282
SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
283
if (socrates_fpga_pic_irq_host == NULL) {
284
pr_err("FPGA PIC: Unable to allocate host\n");
285
return;
286
}
287
288
for (i = 0; i < 3; i++) {
289
socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
290
if (!socrates_fpga_irqs[i]) {
291
pr_warn("FPGA PIC: can't get irq%d\n", i);
292
continue;
293
}
294
irq_set_chained_handler(socrates_fpga_irqs[i],
295
socrates_fpga_pic_cascade);
296
}
297
298
socrates_fpga_pic_iobase = of_iomap(pic, 0);
299
300
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
301
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
302
SOCRATES_FPGA_IRQ_MASK << 16);
303
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
304
SOCRATES_FPGA_IRQ_MASK << 16);
305
socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
306
SOCRATES_FPGA_IRQ_MASK << 16);
307
raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
308
309
pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
310
}
311
312