blob: b02d6a5bb03574a6f1967439d13e2ab4aa70b692 [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>
Rob Herring26a20562013-09-26 07:40:04 -050012#include <linux/of_address.h>
13#include <linux/of_irq.h>
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010014#include <linux/of_platform.h>
15#include <linux/io.h>
16
17/*
18 * The FPGA supports 9 interrupt sources, which can be routed to 3
19 * interrupt request lines of the MPIC. The line to be used can be
20 * specified through the third cell of FDT property "interrupts".
21 */
22
23#define SOCRATES_FPGA_NUM_IRQS 9
24
25#define FPGA_PIC_IRQCFG (0x0)
26#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
27
28#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
29
30struct socrates_fpga_irq_info {
31 unsigned int irq_line;
32 int type;
33};
34
35/*
36 * Interrupt routing and type table
37 *
38 * IRQ_TYPE_NONE means the interrupt type is configurable,
39 * otherwise it's fixed to the specified value.
40 */
41static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
42 [0] = {0, IRQ_TYPE_NONE},
43 [1] = {0, IRQ_TYPE_LEVEL_HIGH},
44 [2] = {0, IRQ_TYPE_LEVEL_LOW},
45 [3] = {0, IRQ_TYPE_NONE},
46 [4] = {0, IRQ_TYPE_NONE},
47 [5] = {0, IRQ_TYPE_NONE},
48 [6] = {0, IRQ_TYPE_NONE},
49 [7] = {0, IRQ_TYPE_NONE},
50 [8] = {0, IRQ_TYPE_LEVEL_HIGH},
51};
52
Anton Vorontsov7e026f72010-02-18 16:45:12 +030053static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010054
55static void __iomem *socrates_fpga_pic_iobase;
Grant Likelybae1d8f2012-02-14 14:06:50 -070056static struct irq_domain *socrates_fpga_pic_irq_host;
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010057static unsigned int socrates_fpga_irqs[3];
58
59static inline uint32_t socrates_fpga_pic_read(int reg)
60{
61 return in_be32(socrates_fpga_pic_iobase + reg);
62}
63
64static inline void socrates_fpga_pic_write(int reg, uint32_t val)
65{
66 out_be32(socrates_fpga_pic_iobase + reg, val);
67}
68
69static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
70{
71 uint32_t cause;
72 unsigned long flags;
73 int i;
74
75 /* Check irq line routed to the MPIC */
76 for (i = 0; i < 3; i++) {
77 if (irq == socrates_fpga_irqs[i])
78 break;
79 }
80 if (i == 3)
81 return NO_IRQ;
82
Anton Vorontsov7e026f72010-02-18 16:45:12 +030083 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010084 cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
Anton Vorontsov7e026f72010-02-18 16:45:12 +030085 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010086 for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
87 if (cause >> (i + 16))
88 break;
89 }
90 return irq_linear_revmap(socrates_fpga_pic_irq_host,
91 (irq_hw_number_t)i);
92}
93
Thomas Gleixnerbd0b9ac2015-09-14 10:42:37 +020094static void socrates_fpga_pic_cascade(struct irq_desc *desc)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010095{
Thomas Gleixnerec775d02011-03-25 16:45:20 +010096 struct irq_chip *chip = irq_desc_get_chip(desc);
Thomas Gleixner0a0dbd92015-09-14 10:13:42 +020097 unsigned int irq = irq_desc_get_irq(desc);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +010098 unsigned int cascade_irq;
99
100 /*
101 * See if we actually have an interrupt, call generic handling code if
102 * we do.
103 */
104 cascade_irq = socrates_fpga_pic_get_irq(irq);
105
106 if (cascade_irq != NO_IRQ)
107 generic_handle_irq(cascade_irq);
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000108 chip->irq_eoi(&desc->irq_data);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100109}
110
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000111static void socrates_fpga_pic_ack(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100112{
113 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000114 unsigned int irq_line, hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100115 uint32_t mask;
116
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100117 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300118 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100119 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
120 & SOCRATES_FPGA_IRQ_MASK;
121 mask |= (1 << (hwirq + 16));
122 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300123 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100124}
125
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000126static void socrates_fpga_pic_mask(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100127{
128 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000129 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100130 int irq_line;
131 u32 mask;
132
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100133 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300134 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100135 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
136 & SOCRATES_FPGA_IRQ_MASK;
137 mask &= ~(1 << hwirq);
138 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300139 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100140}
141
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000142static void socrates_fpga_pic_mask_ack(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100143{
144 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000145 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100146 int irq_line;
147 u32 mask;
148
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100149 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300150 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100151 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
152 & SOCRATES_FPGA_IRQ_MASK;
153 mask &= ~(1 << hwirq);
154 mask |= (1 << (hwirq + 16));
155 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300156 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100157}
158
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000159static void socrates_fpga_pic_unmask(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100160{
161 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000162 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100163 int irq_line;
164 u32 mask;
165
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100166 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300167 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100168 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
169 & SOCRATES_FPGA_IRQ_MASK;
170 mask |= (1 << hwirq);
171 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300172 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100173}
174
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000175static void socrates_fpga_pic_eoi(struct irq_data *d)
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100176{
177 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000178 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100179 int irq_line;
180 u32 mask;
181
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100182 irq_line = fpga_irqs[hwirq].irq_line;
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300183 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100184 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
185 & SOCRATES_FPGA_IRQ_MASK;
186 mask |= (1 << (hwirq + 16));
187 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300188 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100189}
190
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000191static int socrates_fpga_pic_set_type(struct irq_data *d,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100192 unsigned int flow_type)
193{
194 unsigned long flags;
Grant Likely476eb492011-05-04 15:02:15 +1000195 unsigned int hwirq = irqd_to_hwirq(d);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100196 int polarity;
197 u32 mask;
198
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100199 if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
200 return -EINVAL;
201
202 switch (flow_type & IRQ_TYPE_SENSE_MASK) {
203 case IRQ_TYPE_LEVEL_HIGH:
204 polarity = 1;
205 break;
206 case IRQ_TYPE_LEVEL_LOW:
207 polarity = 0;
208 break;
209 default:
210 return -EINVAL;
211 }
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300212 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100213 mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
214 if (polarity)
215 mask |= (1 << hwirq);
216 else
217 mask &= ~(1 << hwirq);
218 socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300219 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100220 return 0;
221}
222
223static struct irq_chip socrates_fpga_pic_chip = {
Anton Blanchardfc380c02010-01-31 20:33:41 +0000224 .name = "FPGA-PIC",
Lennert Buytenhek712d5d72011-03-07 13:59:19 +0000225 .irq_ack = socrates_fpga_pic_ack,
226 .irq_mask = socrates_fpga_pic_mask,
227 .irq_mask_ack = socrates_fpga_pic_mask_ack,
228 .irq_unmask = socrates_fpga_pic_unmask,
229 .irq_eoi = socrates_fpga_pic_eoi,
230 .irq_set_type = socrates_fpga_pic_set_type,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100231};
232
Grant Likelybae1d8f2012-02-14 14:06:50 -0700233static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100234 irq_hw_number_t hwirq)
235{
236 /* All interrupts are LEVEL sensitive */
Thomas Gleixner98488db2011-03-25 15:43:57 +0100237 irq_set_status_flags(virq, IRQ_LEVEL);
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100238 irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
239 handle_fasteoi_irq);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100240
241 return 0;
242}
243
Grant Likelybae1d8f2012-02-14 14:06:50 -0700244static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
Roman Fietze40d50cf2009-12-08 02:39:50 +0000245 struct device_node *ct, const u32 *intspec, unsigned int intsize,
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100246 irq_hw_number_t *out_hwirq, unsigned int *out_flags)
247{
248 struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
249
250 *out_hwirq = intspec[0];
251 if (fpga_irq->type == IRQ_TYPE_NONE) {
252 /* type is configurable */
253 if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
254 intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
255 pr_warning("FPGA PIC: invalid irq type, "
256 "setting default active low\n");
257 *out_flags = IRQ_TYPE_LEVEL_LOW;
258 } else {
259 *out_flags = intspec[1];
260 }
261 } else {
262 /* type is fixed */
263 *out_flags = fpga_irq->type;
264 }
265
266 /* Use specified interrupt routing */
267 if (intspec[2] <= 2)
268 fpga_irq->irq_line = intspec[2];
269 else
270 pr_warning("FPGA PIC: invalid irq routing\n");
271
272 return 0;
273}
274
Grant Likely9f70b8e2012-01-26 12:24:34 -0700275static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100276 .map = socrates_fpga_pic_host_map,
277 .xlate = socrates_fpga_pic_host_xlate,
278};
279
280void socrates_fpga_pic_init(struct device_node *pic)
281{
282 unsigned long flags;
283 int i;
284
Grant Likelybae1d8f2012-02-14 14:06:50 -0700285 /* Setup an irq_domain structure */
Grant Likelya8db8cf2012-02-14 14:06:54 -0700286 socrates_fpga_pic_irq_host = irq_domain_add_linear(pic,
287 SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100288 if (socrates_fpga_pic_irq_host == NULL) {
289 pr_err("FPGA PIC: Unable to allocate host\n");
290 return;
291 }
292
293 for (i = 0; i < 3; i++) {
294 socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
295 if (socrates_fpga_irqs[i] == NO_IRQ) {
296 pr_warning("FPGA PIC: can't get irq%d.\n", i);
297 continue;
298 }
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100299 irq_set_chained_handler(socrates_fpga_irqs[i],
300 socrates_fpga_pic_cascade);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100301 }
302
303 socrates_fpga_pic_iobase = of_iomap(pic, 0);
304
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300305 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100306 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
307 SOCRATES_FPGA_IRQ_MASK << 16);
308 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
309 SOCRATES_FPGA_IRQ_MASK << 16);
310 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
311 SOCRATES_FPGA_IRQ_MASK << 16);
Anton Vorontsov7e026f72010-02-18 16:45:12 +0300312 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
Wolfgang Grandegger393adca2009-03-22 14:58:43 +0100313
314 pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
315}