2 * Driver for MIPS Goldfish Programmable Interrupt Controller.
4 * Author: Miodrag Dinic <miodrag.dinic@mips.com>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/irqchip.h>
17 #include <linux/irqchip/chained_irq.h>
18 #include <linux/irqdomain.h>
19 #include <linux/of_address.h>
20 #include <linux/of_irq.h>
22 #define GFPIC_NR_IRQS 32
24 /* 8..39 Cascaded Goldfish PIC interrupts */
25 #define GFPIC_IRQ_BASE 8
27 #define GFPIC_REG_IRQ_PENDING 0x04
28 #define GFPIC_REG_IRQ_DISABLE_ALL 0x08
29 #define GFPIC_REG_IRQ_DISABLE 0x0c
30 #define GFPIC_REG_IRQ_ENABLE 0x10
32 struct goldfish_pic_data
{
34 struct irq_domain
*irq_domain
;
37 static void goldfish_pic_cascade(struct irq_desc
*desc
)
39 struct goldfish_pic_data
*gfpic
= irq_desc_get_handler_data(desc
);
40 struct irq_chip
*host_chip
= irq_desc_get_chip(desc
);
41 u32 pending
, hwirq
, virq
;
43 chained_irq_enter(host_chip
, desc
);
45 pending
= readl(gfpic
->base
+ GFPIC_REG_IRQ_PENDING
);
47 hwirq
= __fls(pending
);
48 virq
= irq_linear_revmap(gfpic
->irq_domain
, hwirq
);
49 generic_handle_irq(virq
);
50 pending
&= ~(1 << hwirq
);
53 chained_irq_exit(host_chip
, desc
);
56 static const struct irq_domain_ops goldfish_irq_domain_ops
= {
57 .xlate
= irq_domain_xlate_onecell
,
60 static int __init
goldfish_pic_of_init(struct device_node
*of_node
,
61 struct device_node
*parent
)
63 struct goldfish_pic_data
*gfpic
;
64 struct irq_chip_generic
*gc
;
65 struct irq_chip_type
*ct
;
66 unsigned int parent_irq
;
69 gfpic
= kzalloc(sizeof(*gfpic
), GFP_KERNEL
);
75 parent_irq
= irq_of_parse_and_map(of_node
, 0);
77 pr_err("Failed to map parent IRQ!\n");
82 gfpic
->base
= of_iomap(of_node
, 0);
84 pr_err("Failed to map base address!\n");
89 /* Mask interrupts. */
90 writel(1, gfpic
->base
+ GFPIC_REG_IRQ_DISABLE_ALL
);
92 gc
= irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE
, gfpic
->base
,
95 pr_err("Failed to allocate chip structures!\n");
101 ct
->regs
.enable
= GFPIC_REG_IRQ_ENABLE
;
102 ct
->regs
.disable
= GFPIC_REG_IRQ_DISABLE
;
103 ct
->chip
.irq_unmask
= irq_gc_unmask_enable_reg
;
104 ct
->chip
.irq_mask
= irq_gc_mask_disable_reg
;
106 irq_setup_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
), 0,
107 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
109 gfpic
->irq_domain
= irq_domain_add_legacy(of_node
, GFPIC_NR_IRQS
,
111 &goldfish_irq_domain_ops
,
113 if (!gfpic
->irq_domain
) {
114 pr_err("Failed to add irqdomain!\n");
116 goto out_destroy_generic_chip
;
119 irq_set_chained_handler_and_data(parent_irq
,
120 goldfish_pic_cascade
, gfpic
);
122 pr_info("Successfully registered.\n");
125 out_destroy_generic_chip
:
126 irq_destroy_generic_chip(gc
, IRQ_MSK(GFPIC_NR_IRQS
),
127 IRQ_NOPROBE
| IRQ_LEVEL
, 0);
129 iounmap(gfpic
->base
);
131 irq_dispose_mapping(parent_irq
);
135 pr_err("Failed to initialize! (errno = %d)\n", ret
);
139 IRQCHIP_DECLARE(google_gf_pic
, "google,goldfish-pic", goldfish_pic_of_init
);