1/*
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_address.h>
13#include <linux/of_irq.h>
14#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
53static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
54
55static void __iomem *socrates_fpga_pic_iobase;
56static struct irq_domain *socrates_fpga_pic_irq_host;
57static 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
83	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
84	cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
85	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
86	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
94static void socrates_fpga_pic_cascade(struct irq_desc *desc)
95{
96	struct irq_chip *chip = irq_desc_get_chip(desc);
97	unsigned int irq = irq_desc_get_irq(desc);
98	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);
108	chip->irq_eoi(&desc->irq_data);
109}
110
111static void socrates_fpga_pic_ack(struct irq_data *d)
112{
113	unsigned long flags;
114	unsigned int irq_line, hwirq = irqd_to_hwirq(d);
115	uint32_t mask;
116
117	irq_line = fpga_irqs[hwirq].irq_line;
118	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
119	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);
123	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
124}
125
126static void socrates_fpga_pic_mask(struct irq_data *d)
127{
128	unsigned long flags;
129	unsigned int hwirq = irqd_to_hwirq(d);
130	int irq_line;
131	u32 mask;
132
133	irq_line = fpga_irqs[hwirq].irq_line;
134	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
135	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);
139	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
140}
141
142static void socrates_fpga_pic_mask_ack(struct irq_data *d)
143{
144	unsigned long flags;
145	unsigned int hwirq = irqd_to_hwirq(d);
146	int irq_line;
147	u32 mask;
148
149	irq_line = fpga_irqs[hwirq].irq_line;
150	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
151	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);
156	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
157}
158
159static void socrates_fpga_pic_unmask(struct irq_data *d)
160{
161	unsigned long flags;
162	unsigned int hwirq = irqd_to_hwirq(d);
163	int irq_line;
164	u32 mask;
165
166	irq_line = fpga_irqs[hwirq].irq_line;
167	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
168	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);
172	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
173}
174
175static void socrates_fpga_pic_eoi(struct irq_data *d)
176{
177	unsigned long flags;
178	unsigned int hwirq = irqd_to_hwirq(d);
179	int irq_line;
180	u32 mask;
181
182	irq_line = fpga_irqs[hwirq].irq_line;
183	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
184	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);
188	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
189}
190
191static int socrates_fpga_pic_set_type(struct irq_data *d,
192		unsigned int flow_type)
193{
194	unsigned long flags;
195	unsigned int hwirq = irqd_to_hwirq(d);
196	int polarity;
197	u32 mask;
198
199	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	}
212	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
213	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);
219	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
220	return 0;
221}
222
223static struct irq_chip socrates_fpga_pic_chip = {
224	.name		= "FPGA-PIC",
225	.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,
231};
232
233static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
234		irq_hw_number_t hwirq)
235{
236	/* All interrupts are LEVEL sensitive */
237	irq_set_status_flags(virq, IRQ_LEVEL);
238	irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
239				 handle_fasteoi_irq);
240
241	return 0;
242}
243
244static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
245		struct device_node *ct,	const u32 *intspec, unsigned int intsize,
246		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
275static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
276	.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
285	/* Setup an irq_domain structure */
286	socrates_fpga_pic_irq_host = irq_domain_add_linear(pic,
287		    SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
288	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		}
299		irq_set_chained_handler(socrates_fpga_irqs[i],
300					socrates_fpga_pic_cascade);
301	}
302
303	socrates_fpga_pic_iobase = of_iomap(pic, 0);
304
305	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
306	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);
312	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
313
314	pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
315}
316