2 * Copyright (C) 2008 Ilya Yanok, Emcraft Systems
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
11 #include <linux/irq.h>
12 #include <linux/of_platform.h>
16 * The FPGA supports 9 interrupt sources, which can be routed to 3
17 * interrupt request lines of the MPIC. The line to be used can be
18 * specified through the third cell of FDT property "interrupts".
21 #define SOCRATES_FPGA_NUM_IRQS 9
23 #define FPGA_PIC_IRQCFG (0x0)
24 #define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
26 #define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
28 struct socrates_fpga_irq_info
{
29 unsigned int irq_line
;
34 * Interrupt routing and type table
36 * IRQ_TYPE_NONE means the interrupt type is configurable,
37 * otherwise it's fixed to the specified value.
39 static struct socrates_fpga_irq_info fpga_irqs
[SOCRATES_FPGA_NUM_IRQS
] = {
40 [0] = {0, IRQ_TYPE_NONE
},
41 [1] = {0, IRQ_TYPE_LEVEL_HIGH
},
42 [2] = {0, IRQ_TYPE_LEVEL_LOW
},
43 [3] = {0, IRQ_TYPE_NONE
},
44 [4] = {0, IRQ_TYPE_NONE
},
45 [5] = {0, IRQ_TYPE_NONE
},
46 [6] = {0, IRQ_TYPE_NONE
},
47 [7] = {0, IRQ_TYPE_NONE
},
48 [8] = {0, IRQ_TYPE_LEVEL_HIGH
},
51 static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock
);
53 static void __iomem
*socrates_fpga_pic_iobase
;
54 static struct irq_host
*socrates_fpga_pic_irq_host
;
55 static unsigned int socrates_fpga_irqs
[3];
57 static inline uint32_t socrates_fpga_pic_read(int reg
)
59 return in_be32(socrates_fpga_pic_iobase
+ reg
);
62 static inline void socrates_fpga_pic_write(int reg
, uint32_t val
)
64 out_be32(socrates_fpga_pic_iobase
+ reg
, val
);
67 static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq
)
73 /* Check irq line routed to the MPIC */
74 for (i
= 0; i
< 3; i
++) {
75 if (irq
== socrates_fpga_irqs
[i
])
81 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
82 cause
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i
));
83 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
84 for (i
= SOCRATES_FPGA_NUM_IRQS
- 1; i
>= 0; i
--) {
85 if (cause
>> (i
+ 16))
88 return irq_linear_revmap(socrates_fpga_pic_irq_host
,
92 void socrates_fpga_pic_cascade(unsigned int irq
, struct irq_desc
*desc
)
94 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
95 unsigned int cascade_irq
;
98 * See if we actually have an interrupt, call generic handling code if
101 cascade_irq
= socrates_fpga_pic_get_irq(irq
);
103 if (cascade_irq
!= NO_IRQ
)
104 generic_handle_irq(cascade_irq
);
105 chip
->irq_eoi(&desc
->irq_data
);
108 static void socrates_fpga_pic_ack(struct irq_data
*d
)
111 unsigned int irq_line
, hwirq
= irqd_to_hwirq(d
);
114 irq_line
= fpga_irqs
[hwirq
].irq_line
;
115 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
116 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line
))
117 & SOCRATES_FPGA_IRQ_MASK
;
118 mask
|= (1 << (hwirq
+ 16));
119 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line
), mask
);
120 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
123 static void socrates_fpga_pic_mask(struct irq_data
*d
)
126 unsigned int hwirq
= irqd_to_hwirq(d
);
130 irq_line
= fpga_irqs
[hwirq
].irq_line
;
131 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
132 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line
))
133 & SOCRATES_FPGA_IRQ_MASK
;
134 mask
&= ~(1 << hwirq
);
135 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line
), mask
);
136 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
139 static void socrates_fpga_pic_mask_ack(struct irq_data
*d
)
142 unsigned int hwirq
= irqd_to_hwirq(d
);
146 irq_line
= fpga_irqs
[hwirq
].irq_line
;
147 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
148 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line
))
149 & SOCRATES_FPGA_IRQ_MASK
;
150 mask
&= ~(1 << hwirq
);
151 mask
|= (1 << (hwirq
+ 16));
152 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line
), mask
);
153 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
156 static void socrates_fpga_pic_unmask(struct irq_data
*d
)
159 unsigned int hwirq
= irqd_to_hwirq(d
);
163 irq_line
= fpga_irqs
[hwirq
].irq_line
;
164 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
165 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line
))
166 & SOCRATES_FPGA_IRQ_MASK
;
167 mask
|= (1 << hwirq
);
168 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line
), mask
);
169 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
172 static void socrates_fpga_pic_eoi(struct irq_data
*d
)
175 unsigned int hwirq
= irqd_to_hwirq(d
);
179 irq_line
= fpga_irqs
[hwirq
].irq_line
;
180 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
181 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line
))
182 & SOCRATES_FPGA_IRQ_MASK
;
183 mask
|= (1 << (hwirq
+ 16));
184 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line
), mask
);
185 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
188 static int socrates_fpga_pic_set_type(struct irq_data
*d
,
189 unsigned int flow_type
)
192 unsigned int hwirq
= irqd_to_hwirq(d
);
196 if (fpga_irqs
[hwirq
].type
!= IRQ_TYPE_NONE
)
199 switch (flow_type
& IRQ_TYPE_SENSE_MASK
) {
200 case IRQ_TYPE_LEVEL_HIGH
:
203 case IRQ_TYPE_LEVEL_LOW
:
209 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
210 mask
= socrates_fpga_pic_read(FPGA_PIC_IRQCFG
);
212 mask
|= (1 << hwirq
);
214 mask
&= ~(1 << hwirq
);
215 socrates_fpga_pic_write(FPGA_PIC_IRQCFG
, mask
);
216 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
220 static struct irq_chip socrates_fpga_pic_chip
= {
222 .irq_ack
= socrates_fpga_pic_ack
,
223 .irq_mask
= socrates_fpga_pic_mask
,
224 .irq_mask_ack
= socrates_fpga_pic_mask_ack
,
225 .irq_unmask
= socrates_fpga_pic_unmask
,
226 .irq_eoi
= socrates_fpga_pic_eoi
,
227 .irq_set_type
= socrates_fpga_pic_set_type
,
230 static int socrates_fpga_pic_host_map(struct irq_host
*h
, unsigned int virq
,
231 irq_hw_number_t hwirq
)
233 /* All interrupts are LEVEL sensitive */
234 irq_set_status_flags(virq
, IRQ_LEVEL
);
235 irq_set_chip_and_handler(virq
, &socrates_fpga_pic_chip
,
241 static int socrates_fpga_pic_host_xlate(struct irq_host
*h
,
242 struct device_node
*ct
, const u32
*intspec
, unsigned int intsize
,
243 irq_hw_number_t
*out_hwirq
, unsigned int *out_flags
)
245 struct socrates_fpga_irq_info
*fpga_irq
= &fpga_irqs
[intspec
[0]];
247 *out_hwirq
= intspec
[0];
248 if (fpga_irq
->type
== IRQ_TYPE_NONE
) {
249 /* type is configurable */
250 if (intspec
[1] != IRQ_TYPE_LEVEL_LOW
&&
251 intspec
[1] != IRQ_TYPE_LEVEL_HIGH
) {
252 pr_warning("FPGA PIC: invalid irq type, "
253 "setting default active low\n");
254 *out_flags
= IRQ_TYPE_LEVEL_LOW
;
256 *out_flags
= intspec
[1];
260 *out_flags
= fpga_irq
->type
;
263 /* Use specified interrupt routing */
265 fpga_irq
->irq_line
= intspec
[2];
267 pr_warning("FPGA PIC: invalid irq routing\n");
272 static struct irq_host_ops socrates_fpga_pic_host_ops
= {
273 .map
= socrates_fpga_pic_host_map
,
274 .xlate
= socrates_fpga_pic_host_xlate
,
277 void socrates_fpga_pic_init(struct device_node
*pic
)
282 /* Setup an irq_host structure */
283 socrates_fpga_pic_irq_host
= irq_alloc_host(pic
, IRQ_HOST_MAP_LINEAR
,
284 SOCRATES_FPGA_NUM_IRQS
, &socrates_fpga_pic_host_ops
,
285 SOCRATES_FPGA_NUM_IRQS
);
286 if (socrates_fpga_pic_irq_host
== NULL
) {
287 pr_err("FPGA PIC: Unable to allocate host\n");
291 for (i
= 0; i
< 3; i
++) {
292 socrates_fpga_irqs
[i
] = irq_of_parse_and_map(pic
, i
);
293 if (socrates_fpga_irqs
[i
] == NO_IRQ
) {
294 pr_warning("FPGA PIC: can't get irq%d.\n", i
);
297 irq_set_chained_handler(socrates_fpga_irqs
[i
],
298 socrates_fpga_pic_cascade
);
301 socrates_fpga_pic_iobase
= of_iomap(pic
, 0);
303 raw_spin_lock_irqsave(&socrates_fpga_pic_lock
, flags
);
304 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
305 SOCRATES_FPGA_IRQ_MASK
<< 16);
306 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
307 SOCRATES_FPGA_IRQ_MASK
<< 16);
308 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
309 SOCRATES_FPGA_IRQ_MASK
<< 16);
310 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock
, flags
);
312 pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");