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
);
37 u32 pending
, hwirq
, virq
;
39 chained_irq_enter(host_chip
, desc
);
41 pending
= readl(gfpic
->base
+ GFPIC_REG_IRQ_PENDING
);
43 hwirq
= __fls(pending
);
44 virq
= irq_linear_revmap(gfpic
->irq_domain
, hwirq
);
45 generic_handle_irq(virq
);
46 pending
&= ~(1 << hwirq
);
49 chained_irq_exit(host_chip
, desc
);
52 static const struct irq_domain_ops goldfish_irq_domain_ops
= {
53 .xlate
= irq_domain_xlate_onecell
,
56 static int __init
goldfish_pic_of_init(struct device_node
*of_node
,
57 struct device_node
*parent
)
59 struct goldfish_pic_data
*gfpic
;
60 struct irq_chip_generic
*gc
;
61 struct irq_chip_type
*ct
;
62 unsigned int parent_irq
;
65 gfpic
= kzalloc(sizeof(*gfpic
), GFP_KERNEL
);
71 parent_irq
= irq_of_parse_and_map(of_node
, 0);
73 pr_err("Failed to map parent IRQ!\n");
78 gfpic
->base
= of_iomap(of_node
, 0);
80 pr_err("Failed to map base address!\n");
85 /* Mask interrupts. */
86 writel(1, gfpic
->base
+ GFPIC_REG_IRQ_DISABLE_ALL
);
88 gc
= irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE
, gfpic
->base
,
91 pr_err("Failed to allocate chip structures!\n");
97 ct
->regs
.enable
= GFPIC_REG_IRQ_ENABLE
;
98 ct
->regs
.disable
= GFPIC_REG_IRQ_DISABLE
;
99 ct
->chip
.irq_unmask
= irq_gc_unmask_enable_reg
;
100 ct
->chip
.irq_mask
= irq_gc_mask_disable_reg
;
102 irq_setup_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
), 0,
103 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
105 gfpic
->irq_domain
= irq_domain_add_legacy(of_node
, GFPIC_NR_IRQS
,
107 &goldfish_irq_domain_ops
,
109 if (!gfpic
->irq_domain
) {
110 pr_err("Failed to add irqdomain!\n");
112 goto out_destroy_generic_chip
;
115 irq_set_chained_handler_and_data(parent_irq
,
116 goldfish_pic_cascade
, gfpic
);
118 pr_info("Successfully registered.\n");
121 out_destroy_generic_chip
:
122 irq_destroy_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
),
123 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
125 iounmap(gfpic
->base
);
127 irq_dispose_mapping(parent_irq
);
131 pr_err("Failed to initialize! (errno = %d)\n", ret
);
135 IRQCHIP_DECLARE(google_gf_pic
, "google,goldfish-pic", goldfish_pic_of_init
);