1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2393adcacSWolfgang Grandegger /*
3393adcacSWolfgang Grandegger * Copyright (C) 2008 Ilya Yanok, Emcraft Systems
4393adcacSWolfgang Grandegger */
5393adcacSWolfgang Grandegger
6393adcacSWolfgang Grandegger #include <linux/irq.h>
726a2056eSRob Herring #include <linux/of_address.h>
826a2056eSRob Herring #include <linux/of_irq.h>
9393adcacSWolfgang Grandegger #include <linux/io.h>
10393adcacSWolfgang Grandegger
11393adcacSWolfgang Grandegger /*
12393adcacSWolfgang Grandegger * The FPGA supports 9 interrupt sources, which can be routed to 3
13393adcacSWolfgang Grandegger * interrupt request lines of the MPIC. The line to be used can be
14393adcacSWolfgang Grandegger * specified through the third cell of FDT property "interrupts".
15393adcacSWolfgang Grandegger */
16393adcacSWolfgang Grandegger
17393adcacSWolfgang Grandegger #define SOCRATES_FPGA_NUM_IRQS 9
18393adcacSWolfgang Grandegger
19393adcacSWolfgang Grandegger #define FPGA_PIC_IRQCFG (0x0)
20393adcacSWolfgang Grandegger #define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
21393adcacSWolfgang Grandegger
22393adcacSWolfgang Grandegger #define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
23393adcacSWolfgang Grandegger
24393adcacSWolfgang Grandegger struct socrates_fpga_irq_info {
25393adcacSWolfgang Grandegger unsigned int irq_line;
26393adcacSWolfgang Grandegger int type;
27393adcacSWolfgang Grandegger };
28393adcacSWolfgang Grandegger
29393adcacSWolfgang Grandegger /*
30393adcacSWolfgang Grandegger * Interrupt routing and type table
31393adcacSWolfgang Grandegger *
32393adcacSWolfgang Grandegger * IRQ_TYPE_NONE means the interrupt type is configurable,
33393adcacSWolfgang Grandegger * otherwise it's fixed to the specified value.
34393adcacSWolfgang Grandegger */
35393adcacSWolfgang Grandegger static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
36393adcacSWolfgang Grandegger [0] = {0, IRQ_TYPE_NONE},
37393adcacSWolfgang Grandegger [1] = {0, IRQ_TYPE_LEVEL_HIGH},
38393adcacSWolfgang Grandegger [2] = {0, IRQ_TYPE_LEVEL_LOW},
39393adcacSWolfgang Grandegger [3] = {0, IRQ_TYPE_NONE},
40393adcacSWolfgang Grandegger [4] = {0, IRQ_TYPE_NONE},
41393adcacSWolfgang Grandegger [5] = {0, IRQ_TYPE_NONE},
42393adcacSWolfgang Grandegger [6] = {0, IRQ_TYPE_NONE},
43393adcacSWolfgang Grandegger [7] = {0, IRQ_TYPE_NONE},
44393adcacSWolfgang Grandegger [8] = {0, IRQ_TYPE_LEVEL_HIGH},
45393adcacSWolfgang Grandegger };
46393adcacSWolfgang Grandegger
477e026f72SAnton Vorontsov static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
48393adcacSWolfgang Grandegger
49393adcacSWolfgang Grandegger static void __iomem *socrates_fpga_pic_iobase;
50bae1d8f1SGrant Likely static struct irq_domain *socrates_fpga_pic_irq_host;
51393adcacSWolfgang Grandegger static unsigned int socrates_fpga_irqs[3];
52393adcacSWolfgang Grandegger
socrates_fpga_pic_read(int reg)53393adcacSWolfgang Grandegger static inline uint32_t socrates_fpga_pic_read(int reg)
54393adcacSWolfgang Grandegger {
55393adcacSWolfgang Grandegger return in_be32(socrates_fpga_pic_iobase + reg);
56393adcacSWolfgang Grandegger }
57393adcacSWolfgang Grandegger
socrates_fpga_pic_write(int reg,uint32_t val)58393adcacSWolfgang Grandegger static inline void socrates_fpga_pic_write(int reg, uint32_t val)
59393adcacSWolfgang Grandegger {
60393adcacSWolfgang Grandegger out_be32(socrates_fpga_pic_iobase + reg, val);
61393adcacSWolfgang Grandegger }
62393adcacSWolfgang Grandegger
socrates_fpga_pic_get_irq(unsigned int irq)63393adcacSWolfgang Grandegger static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
64393adcacSWolfgang Grandegger {
65393adcacSWolfgang Grandegger uint32_t cause;
66393adcacSWolfgang Grandegger unsigned long flags;
67393adcacSWolfgang Grandegger int i;
68393adcacSWolfgang Grandegger
69393adcacSWolfgang Grandegger /* Check irq line routed to the MPIC */
70393adcacSWolfgang Grandegger for (i = 0; i < 3; i++) {
71393adcacSWolfgang Grandegger if (irq == socrates_fpga_irqs[i])
72393adcacSWolfgang Grandegger break;
73393adcacSWolfgang Grandegger }
74393adcacSWolfgang Grandegger if (i == 3)
75ef24ba70SMichael Ellerman return 0;
76393adcacSWolfgang Grandegger
777e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
78393adcacSWolfgang Grandegger cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
797e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
80393adcacSWolfgang Grandegger for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
81393adcacSWolfgang Grandegger if (cause >> (i + 16))
82393adcacSWolfgang Grandegger break;
83393adcacSWolfgang Grandegger }
84393adcacSWolfgang Grandegger return irq_linear_revmap(socrates_fpga_pic_irq_host,
85393adcacSWolfgang Grandegger (irq_hw_number_t)i);
86393adcacSWolfgang Grandegger }
87393adcacSWolfgang Grandegger
socrates_fpga_pic_cascade(struct irq_desc * desc)88bd0b9ac4SThomas Gleixner static void socrates_fpga_pic_cascade(struct irq_desc *desc)
89393adcacSWolfgang Grandegger {
90ec775d0eSThomas Gleixner struct irq_chip *chip = irq_desc_get_chip(desc);
910a0dbd92SThomas Gleixner unsigned int irq = irq_desc_get_irq(desc);
92393adcacSWolfgang Grandegger unsigned int cascade_irq;
93393adcacSWolfgang Grandegger
94393adcacSWolfgang Grandegger /*
95393adcacSWolfgang Grandegger * See if we actually have an interrupt, call generic handling code if
96393adcacSWolfgang Grandegger * we do.
97393adcacSWolfgang Grandegger */
98393adcacSWolfgang Grandegger cascade_irq = socrates_fpga_pic_get_irq(irq);
99393adcacSWolfgang Grandegger
100ef24ba70SMichael Ellerman if (cascade_irq)
101393adcacSWolfgang Grandegger generic_handle_irq(cascade_irq);
102712d5d79SLennert Buytenhek chip->irq_eoi(&desc->irq_data);
103393adcacSWolfgang Grandegger }
104393adcacSWolfgang Grandegger
socrates_fpga_pic_ack(struct irq_data * d)105712d5d79SLennert Buytenhek static void socrates_fpga_pic_ack(struct irq_data *d)
106393adcacSWolfgang Grandegger {
107393adcacSWolfgang Grandegger unsigned long flags;
108476eb491SGrant Likely unsigned int irq_line, hwirq = irqd_to_hwirq(d);
109393adcacSWolfgang Grandegger uint32_t mask;
110393adcacSWolfgang Grandegger
111393adcacSWolfgang Grandegger irq_line = fpga_irqs[hwirq].irq_line;
1127e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
113393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
114393adcacSWolfgang Grandegger & SOCRATES_FPGA_IRQ_MASK;
115393adcacSWolfgang Grandegger mask |= (1 << (hwirq + 16));
116393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
1177e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
118393adcacSWolfgang Grandegger }
119393adcacSWolfgang Grandegger
socrates_fpga_pic_mask(struct irq_data * d)120712d5d79SLennert Buytenhek static void socrates_fpga_pic_mask(struct irq_data *d)
121393adcacSWolfgang Grandegger {
122393adcacSWolfgang Grandegger unsigned long flags;
123476eb491SGrant Likely unsigned int hwirq = irqd_to_hwirq(d);
124393adcacSWolfgang Grandegger int irq_line;
125393adcacSWolfgang Grandegger u32 mask;
126393adcacSWolfgang Grandegger
127393adcacSWolfgang Grandegger irq_line = fpga_irqs[hwirq].irq_line;
1287e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
129393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
130393adcacSWolfgang Grandegger & SOCRATES_FPGA_IRQ_MASK;
131393adcacSWolfgang Grandegger mask &= ~(1 << hwirq);
132393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
1337e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
134393adcacSWolfgang Grandegger }
135393adcacSWolfgang Grandegger
socrates_fpga_pic_mask_ack(struct irq_data * d)136712d5d79SLennert Buytenhek static void socrates_fpga_pic_mask_ack(struct irq_data *d)
137393adcacSWolfgang Grandegger {
138393adcacSWolfgang Grandegger unsigned long flags;
139476eb491SGrant Likely unsigned int hwirq = irqd_to_hwirq(d);
140393adcacSWolfgang Grandegger int irq_line;
141393adcacSWolfgang Grandegger u32 mask;
142393adcacSWolfgang Grandegger
143393adcacSWolfgang Grandegger irq_line = fpga_irqs[hwirq].irq_line;
1447e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
145393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
146393adcacSWolfgang Grandegger & SOCRATES_FPGA_IRQ_MASK;
147393adcacSWolfgang Grandegger mask &= ~(1 << hwirq);
148393adcacSWolfgang Grandegger mask |= (1 << (hwirq + 16));
149393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
1507e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
151393adcacSWolfgang Grandegger }
152393adcacSWolfgang Grandegger
socrates_fpga_pic_unmask(struct irq_data * d)153712d5d79SLennert Buytenhek static void socrates_fpga_pic_unmask(struct irq_data *d)
154393adcacSWolfgang Grandegger {
155393adcacSWolfgang Grandegger unsigned long flags;
156476eb491SGrant Likely unsigned int hwirq = irqd_to_hwirq(d);
157393adcacSWolfgang Grandegger int irq_line;
158393adcacSWolfgang Grandegger u32 mask;
159393adcacSWolfgang Grandegger
160393adcacSWolfgang Grandegger irq_line = fpga_irqs[hwirq].irq_line;
1617e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
162393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
163393adcacSWolfgang Grandegger & SOCRATES_FPGA_IRQ_MASK;
164393adcacSWolfgang Grandegger mask |= (1 << hwirq);
165393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
1667e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
167393adcacSWolfgang Grandegger }
168393adcacSWolfgang Grandegger
socrates_fpga_pic_eoi(struct irq_data * d)169712d5d79SLennert Buytenhek static void socrates_fpga_pic_eoi(struct irq_data *d)
170393adcacSWolfgang Grandegger {
171393adcacSWolfgang Grandegger unsigned long flags;
172476eb491SGrant Likely unsigned int hwirq = irqd_to_hwirq(d);
173393adcacSWolfgang Grandegger int irq_line;
174393adcacSWolfgang Grandegger u32 mask;
175393adcacSWolfgang Grandegger
176393adcacSWolfgang Grandegger irq_line = fpga_irqs[hwirq].irq_line;
1777e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
178393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
179393adcacSWolfgang Grandegger & SOCRATES_FPGA_IRQ_MASK;
180393adcacSWolfgang Grandegger mask |= (1 << (hwirq + 16));
181393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
1827e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
183393adcacSWolfgang Grandegger }
184393adcacSWolfgang Grandegger
socrates_fpga_pic_set_type(struct irq_data * d,unsigned int flow_type)185712d5d79SLennert Buytenhek static int socrates_fpga_pic_set_type(struct irq_data *d,
186393adcacSWolfgang Grandegger unsigned int flow_type)
187393adcacSWolfgang Grandegger {
188393adcacSWolfgang Grandegger unsigned long flags;
189476eb491SGrant Likely unsigned int hwirq = irqd_to_hwirq(d);
190393adcacSWolfgang Grandegger int polarity;
191393adcacSWolfgang Grandegger u32 mask;
192393adcacSWolfgang Grandegger
193393adcacSWolfgang Grandegger if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
194393adcacSWolfgang Grandegger return -EINVAL;
195393adcacSWolfgang Grandegger
196393adcacSWolfgang Grandegger switch (flow_type & IRQ_TYPE_SENSE_MASK) {
197393adcacSWolfgang Grandegger case IRQ_TYPE_LEVEL_HIGH:
198393adcacSWolfgang Grandegger polarity = 1;
199393adcacSWolfgang Grandegger break;
200393adcacSWolfgang Grandegger case IRQ_TYPE_LEVEL_LOW:
201393adcacSWolfgang Grandegger polarity = 0;
202393adcacSWolfgang Grandegger break;
203393adcacSWolfgang Grandegger default:
204393adcacSWolfgang Grandegger return -EINVAL;
205393adcacSWolfgang Grandegger }
2067e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
207393adcacSWolfgang Grandegger mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
208393adcacSWolfgang Grandegger if (polarity)
209393adcacSWolfgang Grandegger mask |= (1 << hwirq);
210393adcacSWolfgang Grandegger else
211393adcacSWolfgang Grandegger mask &= ~(1 << hwirq);
212393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
2137e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
214393adcacSWolfgang Grandegger return 0;
215393adcacSWolfgang Grandegger }
216393adcacSWolfgang Grandegger
217393adcacSWolfgang Grandegger static struct irq_chip socrates_fpga_pic_chip = {
218b27df672SThomas Gleixner .name = "FPGA-PIC",
219712d5d79SLennert Buytenhek .irq_ack = socrates_fpga_pic_ack,
220712d5d79SLennert Buytenhek .irq_mask = socrates_fpga_pic_mask,
221712d5d79SLennert Buytenhek .irq_mask_ack = socrates_fpga_pic_mask_ack,
222712d5d79SLennert Buytenhek .irq_unmask = socrates_fpga_pic_unmask,
223712d5d79SLennert Buytenhek .irq_eoi = socrates_fpga_pic_eoi,
224712d5d79SLennert Buytenhek .irq_set_type = socrates_fpga_pic_set_type,
225393adcacSWolfgang Grandegger };
226393adcacSWolfgang Grandegger
socrates_fpga_pic_host_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hwirq)227bae1d8f1SGrant Likely static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
228393adcacSWolfgang Grandegger irq_hw_number_t hwirq)
229393adcacSWolfgang Grandegger {
230393adcacSWolfgang Grandegger /* All interrupts are LEVEL sensitive */
23198488db9SThomas Gleixner irq_set_status_flags(virq, IRQ_LEVEL);
232ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
233393adcacSWolfgang Grandegger handle_fasteoi_irq);
234393adcacSWolfgang Grandegger
235393adcacSWolfgang Grandegger return 0;
236393adcacSWolfgang Grandegger }
237393adcacSWolfgang Grandegger
socrates_fpga_pic_host_xlate(struct irq_domain * h,struct device_node * ct,const u32 * intspec,unsigned int intsize,irq_hw_number_t * out_hwirq,unsigned int * out_flags)238bae1d8f1SGrant Likely static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
23940d50cf7SRoman Fietze struct device_node *ct, const u32 *intspec, unsigned int intsize,
240393adcacSWolfgang Grandegger irq_hw_number_t *out_hwirq, unsigned int *out_flags)
241393adcacSWolfgang Grandegger {
242393adcacSWolfgang Grandegger struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
243393adcacSWolfgang Grandegger
244393adcacSWolfgang Grandegger *out_hwirq = intspec[0];
245393adcacSWolfgang Grandegger if (fpga_irq->type == IRQ_TYPE_NONE) {
246393adcacSWolfgang Grandegger /* type is configurable */
247393adcacSWolfgang Grandegger if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
248393adcacSWolfgang Grandegger intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
249f2c2cbccSJoe Perches pr_warn("FPGA PIC: invalid irq type, setting default active low\n");
250393adcacSWolfgang Grandegger *out_flags = IRQ_TYPE_LEVEL_LOW;
251393adcacSWolfgang Grandegger } else {
252393adcacSWolfgang Grandegger *out_flags = intspec[1];
253393adcacSWolfgang Grandegger }
254393adcacSWolfgang Grandegger } else {
255393adcacSWolfgang Grandegger /* type is fixed */
256393adcacSWolfgang Grandegger *out_flags = fpga_irq->type;
257393adcacSWolfgang Grandegger }
258393adcacSWolfgang Grandegger
259393adcacSWolfgang Grandegger /* Use specified interrupt routing */
260393adcacSWolfgang Grandegger if (intspec[2] <= 2)
261393adcacSWolfgang Grandegger fpga_irq->irq_line = intspec[2];
262393adcacSWolfgang Grandegger else
263f2c2cbccSJoe Perches pr_warn("FPGA PIC: invalid irq routing\n");
264393adcacSWolfgang Grandegger
265393adcacSWolfgang Grandegger return 0;
266393adcacSWolfgang Grandegger }
267393adcacSWolfgang Grandegger
2689f70b8ebSGrant Likely static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
269393adcacSWolfgang Grandegger .map = socrates_fpga_pic_host_map,
270393adcacSWolfgang Grandegger .xlate = socrates_fpga_pic_host_xlate,
271393adcacSWolfgang Grandegger };
272393adcacSWolfgang Grandegger
socrates_fpga_pic_init(struct device_node * pic)273*407454caSNick Child void __init socrates_fpga_pic_init(struct device_node *pic)
274393adcacSWolfgang Grandegger {
275393adcacSWolfgang Grandegger unsigned long flags;
276393adcacSWolfgang Grandegger int i;
277393adcacSWolfgang Grandegger
278bae1d8f1SGrant Likely /* Setup an irq_domain structure */
279a8db8cf0SGrant Likely socrates_fpga_pic_irq_host = irq_domain_add_linear(pic,
280a8db8cf0SGrant Likely SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
281393adcacSWolfgang Grandegger if (socrates_fpga_pic_irq_host == NULL) {
282393adcacSWolfgang Grandegger pr_err("FPGA PIC: Unable to allocate host\n");
283393adcacSWolfgang Grandegger return;
284393adcacSWolfgang Grandegger }
285393adcacSWolfgang Grandegger
286393adcacSWolfgang Grandegger for (i = 0; i < 3; i++) {
287393adcacSWolfgang Grandegger socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
288ef24ba70SMichael Ellerman if (!socrates_fpga_irqs[i]) {
289f2c2cbccSJoe Perches pr_warn("FPGA PIC: can't get irq%d\n", i);
290393adcacSWolfgang Grandegger continue;
291393adcacSWolfgang Grandegger }
292ec775d0eSThomas Gleixner irq_set_chained_handler(socrates_fpga_irqs[i],
293393adcacSWolfgang Grandegger socrates_fpga_pic_cascade);
294393adcacSWolfgang Grandegger }
295393adcacSWolfgang Grandegger
296393adcacSWolfgang Grandegger socrates_fpga_pic_iobase = of_iomap(pic, 0);
297393adcacSWolfgang Grandegger
2987e026f72SAnton Vorontsov raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
299393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
300393adcacSWolfgang Grandegger SOCRATES_FPGA_IRQ_MASK << 16);
301393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
302393adcacSWolfgang Grandegger SOCRATES_FPGA_IRQ_MASK << 16);
303393adcacSWolfgang Grandegger socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
304393adcacSWolfgang Grandegger SOCRATES_FPGA_IRQ_MASK << 16);
3057e026f72SAnton Vorontsov raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
306393adcacSWolfgang Grandegger
307393adcacSWolfgang Grandegger pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
308393adcacSWolfgang Grandegger }
309