1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2020 Birger Koblitz <mail@birger-koblitz.de>
4 * Copyright (C) 2020 Bert Vermeulen <bert@biot.com>
5 * Copyright (C) 2020 John Crispin <john@phrozen.org>
8 #include <linux/of_irq.h>
9 #include <linux/irqchip.h>
10 #include <linux/spinlock.h>
11 #include <linux/of_address.h>
12 #include <linux/irqchip/chained_irq.h>
14 /* Global Interrupt Mask Register */
15 #define RTL_ICTL_GIMR 0x00
16 /* Global Interrupt Status Register */
17 #define RTL_ICTL_GISR 0x04
18 /* Interrupt Routing Registers */
19 #define RTL_ICTL_IRR0 0x08
20 #define RTL_ICTL_IRR1 0x0c
21 #define RTL_ICTL_IRR2 0x10
22 #define RTL_ICTL_IRR3 0x14
24 #define REG(x) (realtek_ictl_base + x)
26 static DEFINE_RAW_SPINLOCK(irq_lock
);
27 static void __iomem
*realtek_ictl_base
;
29 static void realtek_ictl_unmask_irq(struct irq_data
*i
)
34 raw_spin_lock_irqsave(&irq_lock
, flags
);
36 value
= readl(REG(RTL_ICTL_GIMR
));
37 value
|= BIT(i
->hwirq
);
38 writel(value
, REG(RTL_ICTL_GIMR
));
40 raw_spin_unlock_irqrestore(&irq_lock
, flags
);
43 static void realtek_ictl_mask_irq(struct irq_data
*i
)
48 raw_spin_lock_irqsave(&irq_lock
, flags
);
50 value
= readl(REG(RTL_ICTL_GIMR
));
51 value
&= ~BIT(i
->hwirq
);
52 writel(value
, REG(RTL_ICTL_GIMR
));
54 raw_spin_unlock_irqrestore(&irq_lock
, flags
);
57 static struct irq_chip realtek_ictl_irq
= {
58 .name
= "realtek-rtl-intc",
59 .irq_mask
= realtek_ictl_mask_irq
,
60 .irq_unmask
= realtek_ictl_unmask_irq
,
63 static int intc_map(struct irq_domain
*d
, unsigned int irq
, irq_hw_number_t hw
)
65 irq_set_chip_and_handler(irq
, &realtek_ictl_irq
, handle_level_irq
);
70 static const struct irq_domain_ops irq_domain_ops
= {
72 .xlate
= irq_domain_xlate_onecell
,
75 static void realtek_irq_dispatch(struct irq_desc
*desc
)
77 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
78 struct irq_domain
*domain
;
81 chained_irq_enter(chip
, desc
);
82 pending
= readl(REG(RTL_ICTL_GIMR
)) & readl(REG(RTL_ICTL_GISR
));
83 if (unlikely(!pending
)) {
87 domain
= irq_desc_get_handler_data(desc
);
88 generic_handle_domain_irq(domain
, __ffs(pending
));
91 chained_irq_exit(chip
, desc
);
95 * SoC interrupts are cascaded to MIPS CPU interrupts according to the
96 * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
97 * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
98 * thus go into 4 IRRs. A routing value of '0' means the interrupt is left
99 * disconnected. Routing values {1..15} connect to output lines {0..14}.
101 static int __init
map_interrupts(struct device_node
*node
, struct irq_domain
*domain
)
103 struct device_node
*cpu_ictl
;
105 u32 imaplen
, soc_int
, cpu_int
, tmp
, regs
[4];
106 int ret
, i
, irr_regs
[] = {
114 ret
= of_property_read_u32(node
, "#address-cells", &tmp
);
118 imap
= of_get_property(node
, "interrupt-map", &imaplen
);
119 if (!imap
|| imaplen
% 3)
123 memset(regs
, 0, sizeof(regs
));
124 for (i
= 0; i
< imaplen
; i
+= 3 * sizeof(u32
)) {
125 soc_int
= be32_to_cpup(imap
);
129 cpu_ictl
= of_find_node_by_phandle(be32_to_cpup(imap
+ 1));
132 ret
= of_property_read_u32(cpu_ictl
, "#interrupt-cells", &tmp
);
135 of_node_put(cpu_ictl
);
137 cpu_int
= be32_to_cpup(imap
+ 2);
138 if (cpu_int
> 7 || cpu_int
< 2)
141 if (!(mips_irqs_set
& BIT(cpu_int
))) {
142 irq_set_chained_handler_and_data(cpu_int
, realtek_irq_dispatch
,
144 mips_irqs_set
|= BIT(cpu_int
);
147 /* Use routing values (1..6) for CPU interrupts (2..7) */
148 regs
[(soc_int
* 4) / 32] |= (cpu_int
- 1) << (soc_int
* 4) % 32;
152 for (i
= 0; i
< 4; i
++)
153 writel(regs
[i
], REG(irr_regs
[i
]));
158 static int __init
realtek_rtl_of_init(struct device_node
*node
, struct device_node
*parent
)
160 struct irq_domain
*domain
;
163 realtek_ictl_base
= of_iomap(node
, 0);
164 if (!realtek_ictl_base
)
167 /* Disable all cascaded interrupts */
168 writel(0, REG(RTL_ICTL_GIMR
));
170 domain
= irq_domain_add_simple(node
, 32, 0,
171 &irq_domain_ops
, NULL
);
173 ret
= map_interrupts(node
, domain
);
175 pr_err("invalid interrupt map\n");
182 IRQCHIP_DECLARE(realtek_rtl_intc
, "realtek,rtl-intc", realtek_rtl_of_init
);