File Index
Files and directories
Sign in
FILE

socrates_fpga_pic.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/85xx/socrates_fpga_pic.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  Copyright (C) 2008 Ilya Yanok, Emcraft Systems
  4 */
  5
  6#include <linux/irq.h>
  7#include <linux/of_address.h>
  8#include <linux/of_irq.h>
  9#include <linux/io.h>
 10
 11#include "socrates_fpga_pic.h"
 12
 13/*
 14 * The FPGA supports 9 interrupt sources, which can be routed to 3
 15 * interrupt request lines of the MPIC. The line to be used can be
 16 * specified through the third cell of FDT property  "interrupts".
 17 */
 18
 19#define SOCRATES_FPGA_NUM_IRQS	9
 20
 21#define FPGA_PIC_IRQCFG		(0x0)
 22#define FPGA_PIC_IRQMASK(n)	(0x4 + 0x4 * (n))
 23
 24#define SOCRATES_FPGA_IRQ_MASK	((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
 25
 26struct socrates_fpga_irq_info {
 27	unsigned int irq_line;
 28	int type;
 29};
 30
 31/*
 32 * Interrupt routing and type table
 33 *
 34 * IRQ_TYPE_NONE means the interrupt type is configurable,
 35 * otherwise it's fixed to the specified value.
 36 */
 37static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
 38	[0] = {0, IRQ_TYPE_NONE},
 39	[1] = {0, IRQ_TYPE_LEVEL_HIGH},
 40	[2] = {0, IRQ_TYPE_LEVEL_LOW},
 41	[3] = {0, IRQ_TYPE_NONE},
 42	[4] = {0, IRQ_TYPE_NONE},
 43	[5] = {0, IRQ_TYPE_NONE},
 44	[6] = {0, IRQ_TYPE_NONE},
 45	[7] = {0, IRQ_TYPE_NONE},
 46	[8] = {0, IRQ_TYPE_LEVEL_HIGH},
 47};
 48
 49static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
 50
 51static void __iomem *socrates_fpga_pic_iobase;
 52static struct irq_domain *socrates_fpga_pic_irq_host;
 53static unsigned int socrates_fpga_irqs[3];
 54
 55static inline uint32_t socrates_fpga_pic_read(int reg)
 56{
 57	return in_be32(socrates_fpga_pic_iobase + reg);
 58}
 59
 60static inline void socrates_fpga_pic_write(int reg, uint32_t val)
 61{
 62	out_be32(socrates_fpga_pic_iobase + reg, val);
 63}
 64
 65static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
 66{
 67	uint32_t cause;
 68	unsigned long flags;
 69	int i;
 70
 71	/* Check irq line routed to the MPIC */
 72	for (i = 0; i < 3; i++) {
 73		if (irq == socrates_fpga_irqs[i])
 74			break;
 75	}
 76	if (i == 3)
 77		return 0;
 78
 79	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 80	cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
 81	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 82	for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
 83		if (cause >> (i + 16))
 84			break;
 85	}
 86	return irq_find_mapping(socrates_fpga_pic_irq_host,
 87			(irq_hw_number_t)i);
 88}
 89
 90static void socrates_fpga_pic_cascade(struct irq_desc *desc)
 91{
 92	struct irq_chip *chip = irq_desc_get_chip(desc);
 93	unsigned int irq = irq_desc_get_irq(desc);
 94	unsigned int cascade_irq;
 95
 96	/*
 97	 * See if we actually have an interrupt, call generic handling code if
 98	 * we do.
 99	 */
100	cascade_irq = socrates_fpga_pic_get_irq(irq);
101
102	if (cascade_irq)
103		generic_handle_irq(cascade_irq);
104	chip->irq_eoi(&desc->irq_data);
105}
106
107static void socrates_fpga_pic_ack(struct irq_data *d)
108{
109	unsigned long flags;
110	unsigned int irq_line, hwirq = irqd_to_hwirq(d);
111	uint32_t mask;
112
113	irq_line = fpga_irqs[hwirq].irq_line;
114	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
115	mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
116		& SOCRATES_FPGA_IRQ_MASK;
117	mask |= (1 << (hwirq + 16));
118	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
119	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
120}
121
122static void socrates_fpga_pic_mask(struct irq_data *d)
123{
124	unsigned long flags;
125	unsigned int hwirq = irqd_to_hwirq(d);
126	int irq_line;
127	u32 mask;
128
129	irq_line = fpga_irqs[hwirq].irq_line;
130	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
131	mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
132		& SOCRATES_FPGA_IRQ_MASK;
133	mask &= ~(1 << hwirq);
134	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
135	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
136}
137
138static void socrates_fpga_pic_mask_ack(struct irq_data *d)
139{
140	unsigned long flags;
141	unsigned int hwirq = irqd_to_hwirq(d);
142	int irq_line;
143	u32 mask;
144
145	irq_line = fpga_irqs[hwirq].irq_line;
146	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
147	mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
148		& SOCRATES_FPGA_IRQ_MASK;
149	mask &= ~(1 << hwirq);
150	mask |= (1 << (hwirq + 16));
151	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
152	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
153}
154
155static void socrates_fpga_pic_unmask(struct irq_data *d)
156{
157	unsigned long flags;
158	unsigned int hwirq = irqd_to_hwirq(d);
159	int irq_line;
160	u32 mask;
161
162	irq_line = fpga_irqs[hwirq].irq_line;
163	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
164	mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
165		& SOCRATES_FPGA_IRQ_MASK;
166	mask |= (1 << hwirq);
167	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
168	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
169}
170
171static void socrates_fpga_pic_eoi(struct irq_data *d)
172{
173	unsigned long flags;
174	unsigned int hwirq = irqd_to_hwirq(d);
175	int irq_line;
176	u32 mask;
177
178	irq_line = fpga_irqs[hwirq].irq_line;
179	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
180	mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
181		& SOCRATES_FPGA_IRQ_MASK;
182	mask |= (1 << (hwirq + 16));
183	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
184	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
185}
186
187static int socrates_fpga_pic_set_type(struct irq_data *d,
188		unsigned int flow_type)
189{
190	unsigned long flags;
191	unsigned int hwirq = irqd_to_hwirq(d);
192	int polarity;
193	u32 mask;
194
195	if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
196		return -EINVAL;
197
198	switch (flow_type & IRQ_TYPE_SENSE_MASK) {
199	case IRQ_TYPE_LEVEL_HIGH:
200		polarity = 1;
201		break;
202	case IRQ_TYPE_LEVEL_LOW:
203		polarity = 0;
204		break;
205	default:
206		return -EINVAL;
207	}
208	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
209	mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
210	if (polarity)
211		mask |= (1 << hwirq);
212	else
213		mask &= ~(1 << hwirq);
214	socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
215	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
216	return 0;
217}
218
219static struct irq_chip socrates_fpga_pic_chip = {
220	.name		= "FPGA-PIC",
221	.irq_ack	= socrates_fpga_pic_ack,
222	.irq_mask	= socrates_fpga_pic_mask,
223	.irq_mask_ack	= socrates_fpga_pic_mask_ack,
224	.irq_unmask	= socrates_fpga_pic_unmask,
225	.irq_eoi	= socrates_fpga_pic_eoi,
226	.irq_set_type	= socrates_fpga_pic_set_type,
227};
228
229static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
230		irq_hw_number_t hwirq)
231{
232	/* All interrupts are LEVEL sensitive */
233	irq_set_status_flags(virq, IRQ_LEVEL);
234	irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
235				 handle_fasteoi_irq);
236
237	return 0;
238}
239
240static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
241		struct device_node *ct,	const u32 *intspec, unsigned int intsize,
242		irq_hw_number_t *out_hwirq, unsigned int *out_flags)
243{
244	struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
245
246	*out_hwirq = intspec[0];
247	if  (fpga_irq->type == IRQ_TYPE_NONE) {
248		/* type is configurable */
249		if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
250		    intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
251			pr_warn("FPGA PIC: invalid irq type, setting default active low\n");
252			*out_flags = IRQ_TYPE_LEVEL_LOW;
253		} else {
254			*out_flags = intspec[1];
255		}
256	} else {
257		/* type is fixed */
258		*out_flags = fpga_irq->type;
259	}
260
261	/* Use specified interrupt routing */
262	if (intspec[2] <= 2)
263		fpga_irq->irq_line = intspec[2];
264	else
265		pr_warn("FPGA PIC: invalid irq routing\n");
266
267	return 0;
268}
269
270static const struct irq_domain_ops socrates_fpga_pic_host_ops = {
271	.map    = socrates_fpga_pic_host_map,
272	.xlate  = socrates_fpga_pic_host_xlate,
273};
274
275void __init socrates_fpga_pic_init(struct device_node *pic)
276{
277	unsigned long flags;
278	int i;
279
280	/* Setup an irq_domain structure */
281	socrates_fpga_pic_irq_host = irq_domain_create_linear(of_fwnode_handle(pic),
282		    SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);
283	if (socrates_fpga_pic_irq_host == NULL) {
284		pr_err("FPGA PIC: Unable to allocate host\n");
285		return;
286	}
287
288	for (i = 0; i < 3; i++) {
289		socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
290		if (!socrates_fpga_irqs[i]) {
291			pr_warn("FPGA PIC: can't get irq%d\n", i);
292			continue;
293		}
294		irq_set_chained_handler(socrates_fpga_irqs[i],
295					socrates_fpga_pic_cascade);
296	}
297
298	socrates_fpga_pic_iobase = of_iomap(pic, 0);
299
300	raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
301	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
302			SOCRATES_FPGA_IRQ_MASK << 16);
303	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
304			SOCRATES_FPGA_IRQ_MASK << 16);
305	socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
306			SOCRATES_FPGA_IRQ_MASK << 16);
307	raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
308
309	pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
310}
~/ Read-only file preview.