1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for MIPS Goldfish Programmable Interrupt Controller.
5 * Author: Miodrag Dinic <miodrag.dinic@mips.com>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/interrupt.h>
11 #include <linux/irq.h>
12 #include <linux/irqchip.h>
13 #include <linux/irqchip/chained_irq.h>
14 #include <linux/irqdomain.h>
15 #include <linux/of_address.h>
16 #include <linux/of_irq.h>
18 #define GFPIC_NR_IRQS 32
20 /* 8..39 Cascaded Goldfish PIC interrupts */
21 #define GFPIC_IRQ_BASE 8
23 #define GFPIC_REG_IRQ_PENDING 0x04
24 #define GFPIC_REG_IRQ_DISABLE_ALL 0x08
25 #define GFPIC_REG_IRQ_DISABLE 0x0c
26 #define GFPIC_REG_IRQ_ENABLE 0x10
28 struct goldfish_pic_data
{
30 struct irq_domain
*irq_domain
;
33 static void goldfish_pic_cascade(struct irq_desc
*desc
)
35 struct goldfish_pic_data
*gfpic
= irq_desc_get_handler_data(desc
);
36 struct irq_chip
*host_chip
= irq_desc_get_chip(desc
);
39 chained_irq_enter(host_chip
, desc
);
41 pending
= readl(gfpic
->base
+ GFPIC_REG_IRQ_PENDING
);
43 hwirq
= __fls(pending
);
44 generic_handle_domain_irq(gfpic
->irq_domain
, hwirq
);
45 pending
&= ~(1 << hwirq
);
48 chained_irq_exit(host_chip
, desc
);
51 static const struct irq_domain_ops goldfish_irq_domain_ops
= {
52 .xlate
= irq_domain_xlate_onecell
,
55 static int __init
goldfish_pic_of_init(struct device_node
*of_node
,
56 struct device_node
*parent
)
58 struct goldfish_pic_data
*gfpic
;
59 struct irq_chip_generic
*gc
;
60 struct irq_chip_type
*ct
;
61 unsigned int parent_irq
;
64 gfpic
= kzalloc(sizeof(*gfpic
), GFP_KERNEL
);
70 parent_irq
= irq_of_parse_and_map(of_node
, 0);
72 pr_err("Failed to map parent IRQ!\n");
77 gfpic
->base
= of_iomap(of_node
, 0);
79 pr_err("Failed to map base address!\n");
84 /* Mask interrupts. */
85 writel(1, gfpic
->base
+ GFPIC_REG_IRQ_DISABLE_ALL
);
87 gc
= irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE
, gfpic
->base
,
90 pr_err("Failed to allocate chip structures!\n");
96 ct
->regs
.enable
= GFPIC_REG_IRQ_ENABLE
;
97 ct
->regs
.disable
= GFPIC_REG_IRQ_DISABLE
;
98 ct
->chip
.irq_unmask
= irq_gc_unmask_enable_reg
;
99 ct
->chip
.irq_mask
= irq_gc_mask_disable_reg
;
101 irq_setup_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
), 0,
102 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
104 gfpic
->irq_domain
= irq_domain_add_legacy(of_node
, GFPIC_NR_IRQS
,
106 &goldfish_irq_domain_ops
,
108 if (!gfpic
->irq_domain
) {
109 pr_err("Failed to add irqdomain!\n");
111 goto out_destroy_generic_chip
;
114 irq_set_chained_handler_and_data(parent_irq
,
115 goldfish_pic_cascade
, gfpic
);
117 pr_info("Successfully registered.\n");
120 out_destroy_generic_chip
:
121 irq_destroy_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
),
122 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
124 iounmap(gfpic
->base
);
126 irq_dispose_mapping(parent_irq
);
130 pr_err("Failed to initialize! (errno = %d)\n", ret
);
134 IRQCHIP_DECLARE(google_gf_pic
, "google,goldfish-pic", goldfish_pic_of_init
);