blob: 3bbbf74894872b140a1aef210315c4da10a4da48 [file] [log] [blame]
Wolfgang Grandegger393adca2009-03-22 14:58:43 +01001/*
2 * Copyright (C) 2008 Ilya Yanok, Emcraft Systems
3 *
4 *
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.
8 *
9 */
10
11#include <linux/irq.h>
12#include <linux/of_platform.h>
13#include <linux/io.h>
14
15/*
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".
19 */
20
21#define SOCRATES_FPGA_NUM_IRQS 9
22
23#define FPGA_PIC_IRQCFG (0x0)
24#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
25
26#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
27
28struct socrates_fpga_irq_info {
29 unsigned int irq_line;
30 int type;
31};
32
33/*
34 * Interrupt routing and type table
35 *
36 * IRQ_TYPE_NONE means the interrupt type is configurable,
37 * otherwise it's fixed to the specified value.
38 */
39static 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},
49};
50
Anton Vorontsov7e026f72010-02-18 16:45:12 +030051static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010052
53static void __iomem *socrates_fpga_pic_iobase;
Grant Likelybae1d8f2012-02-14 14:06:50 -070054static struct irq_domain *socrates_fpga_pic_irq_host;
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010055static unsigned int socrates_fpga_irqs[3];
56
57static inline uint32_t socrates_fpga_pic_read(int reg)
58{
59 return in_be32(socrates_fpga_pic_iobase + reg);
60}
61
62static inline void socrates_fpga_pic_write(int reg, uint32_t val)
63{
64 out_be32(socrates_fpga_pic_iobase + reg, val);
65}
66
67static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
68{
69 uint32_t cause;
70 unsigned long flags;
71 int i;
72
73 /* Check irq line routed to the MPIC */
74 for (i = 0; i < 3; i++) {
75 if (irq == socrates_fpga_irqs[i])
76 break;
77 }
78 if (i == 3)
79 return NO_IRQ;
80
Anton Vorontsov7e026f72010-02-18 16:45:12 +030081 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010082 cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
Anton Vorontsov7e026f72010-02-18 16:45:12 +030083 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010084 for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
85 if (cause >> (i + 16))
86 break;
87 }
88 return irq_linear_revmap(socrates_fpga_pic_irq_host,
89 (irq_hw_number_t)i);
90}
91
92void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc)
93{
Thomas Gleixnerec775d02011-03-25 16:45:20 +010094 struct irq_chip *chip = irq_desc_get_chip(desc);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010095 unsigned int cascade_irq;
96
97 /*
98 * See if we actually have an interrupt, call generic handling code if
99 * we do.
100 */
101 cascade_irq = socrates_fpga_pic_get_irq(irq);
102
103 if (cascade_irq != NO_IRQ)
104 generic_handle_irq(cascade_irq);
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000105 chip->irq_eoi(&desc->irq_data);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100106}
107
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000108static void socrates_fpga_pic_ack(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100109{
110 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000111 unsigned int irq_line, hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100112 uint32_t mask;
113
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100114 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300115 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100116 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);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300120 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100121}
122
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000123static void socrates_fpga_pic_mask(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100124{
125 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000126 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100127 int irq_line;
128 u32 mask;
129
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100130 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300131 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100132 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);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300136 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100137}
138
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000139static void socrates_fpga_pic_mask_ack(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100140{
141 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000142 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100143 int irq_line;
144 u32 mask;
145
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100146 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300147 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100148 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);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300153 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100154}
155
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000156static void socrates_fpga_pic_unmask(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100157{
158 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000159 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100160 int irq_line;
161 u32 mask;
162
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100163 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300164 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100165 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);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300169 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100170}
171
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000172static void socrates_fpga_pic_eoi(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100173{
174 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000175 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100176 int irq_line;
177 u32 mask;
178
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100179 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300180 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100181 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);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300185 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100186}
187
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000188static int socrates_fpga_pic_set_type(struct irq_data *d,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100189 unsigned int flow_type)
190{
191 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000192 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100193 int polarity;
194 u32 mask;
195
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100196 if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
197 return -EINVAL;
198
199 switch (flow_type & IRQ_TYPE_SENSE_MASK) {
200 case IRQ_TYPE_LEVEL_HIGH:
201 polarity = 1;
202 break;
203 case IRQ_TYPE_LEVEL_LOW:
204 polarity = 0;
205 break;
206 default:
207 return -EINVAL;
208 }
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300209 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100210 mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
211 if (polarity)
212 mask |= (1 << hwirq);
213 else
214 mask &= ~(1 << hwirq);
215 socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300216 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100217 return 0;
218}
219
220static struct irq_chip socrates_fpga_pic_chip = {
Anton Blanchardfc380c02010-01-31 20:33:41 +0000221 .name = "FPGA-PIC",
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000222 .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,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100228};
229
Grant Likelybae1d8f2012-02-14 14:06:50 -0700230static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100231 irq_hw_number_t hwirq)
232{
233 /* All interrupts are LEVEL sensitive */
Thomas Gleixner98488db2011-03-25 15:43:57 +0100234 irq_set_status_flags(virq, IRQ_LEVEL);
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100235 irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
236 handle_fasteoi_irq);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100237
238 return 0;
239}
240
Grant Likelybae1d8f2012-02-14 14:06:50 -0700241static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
Roman Fietze40d50cf2009-12-08 02:39:50 +0000242 struct device_node *ct, const u32 *intspec, unsigned int intsize,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100243 irq_hw_number_t *out_hwirq, unsigned int *out_flags)
244{
245 struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
246
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;
255 } else {
256 *out_flags = intspec[1];
257 }
258 } else {
259 /* type is fixed */
260 *out_flags = fpga_irq->type;
261 }
262
263 /* Use specified interrupt routing */
264 if (intspec[2] <= 2)
265 fpga_irq->irq_line = intspec[2];
266 else
267 pr_warning("FPGA PIC: invalid irq routing\n");
268
269 return 0;
270}
271
Grant Likely9f70b8e2012-01-26 12:24:34 -0700272static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100273 .map = socrates_fpga_pic_host_map,
274 .xlate = socrates_fpga_pic_host_xlate,
275};
276
277void socrates_fpga_pic_init(struct device_node *pic)
278{
279 unsigned long flags;
280 int i;
281
Grant Likelybae1d8f2012-02-14 14:06:50 -0700282 /* Setup an irq_domain structure */
Grant Likelya8db8cf2012-02-14 14:06:54 -0700283 socrates_fpga_pic_irq_host = irq_domain_add_linear(pic,
284 SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100285 if (socrates_fpga_pic_irq_host == NULL) {
286 pr_err("FPGA PIC: Unable to allocate host\n");
287 return;
288 }
289
290 for (i = 0; i < 3; i++) {
291 socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
292 if (socrates_fpga_irqs[i] == NO_IRQ) {
293 pr_warning("FPGA PIC: can't get irq%d.\n", i);
294 continue;
295 }
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100296 irq_set_chained_handler(socrates_fpga_irqs[i],
297 socrates_fpga_pic_cascade);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100298 }
299
300 socrates_fpga_pic_iobase = of_iomap(pic, 0);
301
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300302 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100303 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
304 SOCRATES_FPGA_IRQ_MASK << 16);
305 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
306 SOCRATES_FPGA_IRQ_MASK << 16);
307 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
308 SOCRATES_FPGA_IRQ_MASK << 16);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300309 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100310
311 pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
312}