1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
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> |
6 | */ |
7 | |
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> |
13 | |
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 |
23 | |
24 | #define RTL_ICTL_NUM_INPUTS 32 |
25 | |
26 | #define REG(x) (realtek_ictl_base + x) |
27 | |
28 | static DEFINE_RAW_SPINLOCK(irq_lock); |
29 | static void __iomem *realtek_ictl_base; |
30 | |
31 | /* |
32 | * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, |
33 | * placing IRQ 31 in the first four bits. A routing value of '0' means the |
34 | * interrupt is left disconnected. Routing values {1..15} connect to output |
35 | * lines {0..14}. |
36 | */ |
37 | #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) |
38 | #define IRR_SHIFT(idx) ((idx * 4) % 32) |
39 | |
40 | static void write_irr(void __iomem *irr0, int idx, u32 value) |
41 | { |
42 | unsigned int offset = IRR_OFFSET(idx); |
43 | unsigned int shift = IRR_SHIFT(idx); |
44 | u32 irr; |
45 | |
46 | irr = readl(addr: irr0 + offset) & ~(0xf << shift); |
47 | irr |= (value & 0xf) << shift; |
48 | writel(val: irr, addr: irr0 + offset); |
49 | } |
50 | |
51 | static void realtek_ictl_unmask_irq(struct irq_data *i) |
52 | { |
53 | unsigned long flags; |
54 | u32 value; |
55 | |
56 | raw_spin_lock_irqsave(&irq_lock, flags); |
57 | |
58 | value = readl(REG(RTL_ICTL_GIMR)); |
59 | value |= BIT(i->hwirq); |
60 | writel(val: value, REG(RTL_ICTL_GIMR)); |
61 | |
62 | raw_spin_unlock_irqrestore(&irq_lock, flags); |
63 | } |
64 | |
65 | static void realtek_ictl_mask_irq(struct irq_data *i) |
66 | { |
67 | unsigned long flags; |
68 | u32 value; |
69 | |
70 | raw_spin_lock_irqsave(&irq_lock, flags); |
71 | |
72 | value = readl(REG(RTL_ICTL_GIMR)); |
73 | value &= ~BIT(i->hwirq); |
74 | writel(val: value, REG(RTL_ICTL_GIMR)); |
75 | |
76 | raw_spin_unlock_irqrestore(&irq_lock, flags); |
77 | } |
78 | |
79 | static struct irq_chip realtek_ictl_irq = { |
80 | .name = "realtek-rtl-intc" , |
81 | .irq_mask = realtek_ictl_mask_irq, |
82 | .irq_unmask = realtek_ictl_unmask_irq, |
83 | }; |
84 | |
85 | static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) |
86 | { |
87 | unsigned long flags; |
88 | |
89 | irq_set_chip_and_handler(irq, chip: &realtek_ictl_irq, handle: handle_level_irq); |
90 | |
91 | raw_spin_lock_irqsave(&irq_lock, flags); |
92 | write_irr(REG(RTL_ICTL_IRR0), idx: hw, value: 1); |
93 | raw_spin_unlock_irqrestore(&irq_lock, flags); |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | static const struct irq_domain_ops irq_domain_ops = { |
99 | .map = intc_map, |
100 | .xlate = irq_domain_xlate_onecell, |
101 | }; |
102 | |
103 | static void realtek_irq_dispatch(struct irq_desc *desc) |
104 | { |
105 | struct irq_chip *chip = irq_desc_get_chip(desc); |
106 | struct irq_domain *domain; |
107 | unsigned long pending; |
108 | unsigned int soc_int; |
109 | |
110 | chained_irq_enter(chip, desc); |
111 | pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); |
112 | |
113 | if (unlikely(!pending)) { |
114 | spurious_interrupt(); |
115 | goto out; |
116 | } |
117 | |
118 | domain = irq_desc_get_handler_data(desc); |
119 | for_each_set_bit(soc_int, &pending, 32) |
120 | generic_handle_domain_irq(domain, hwirq: soc_int); |
121 | |
122 | out: |
123 | chained_irq_exit(chip, desc); |
124 | } |
125 | |
126 | static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) |
127 | { |
128 | struct of_phandle_args oirq; |
129 | struct irq_domain *domain; |
130 | unsigned int soc_irq; |
131 | int parent_irq; |
132 | |
133 | realtek_ictl_base = of_iomap(node, index: 0); |
134 | if (!realtek_ictl_base) |
135 | return -ENXIO; |
136 | |
137 | /* Disable all cascaded interrupts and clear routing */ |
138 | writel(val: 0, REG(RTL_ICTL_GIMR)); |
139 | for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) |
140 | write_irr(REG(RTL_ICTL_IRR0), idx: soc_irq, value: 0); |
141 | |
142 | if (WARN_ON(!of_irq_count(node))) { |
143 | /* |
144 | * If DT contains no parent interrupts, assume MIPS CPU IRQ 2 |
145 | * (HW0) is connected to the first output. This is the case for |
146 | * all known hardware anyway. "interrupt-map" is deprecated, so |
147 | * don't bother trying to parse that. |
148 | */ |
149 | oirq.np = of_find_compatible_node(NULL, NULL, compat: "mti,cpu-interrupt-controller" ); |
150 | oirq.args_count = 1; |
151 | oirq.args[0] = 2; |
152 | |
153 | parent_irq = irq_create_of_mapping(irq_data: &oirq); |
154 | |
155 | of_node_put(node: oirq.np); |
156 | } else { |
157 | parent_irq = of_irq_get(dev: node, index: 0); |
158 | } |
159 | |
160 | if (parent_irq < 0) |
161 | return parent_irq; |
162 | else if (!parent_irq) |
163 | return -ENODEV; |
164 | |
165 | domain = irq_domain_add_linear(of_node: node, RTL_ICTL_NUM_INPUTS, ops: &irq_domain_ops, NULL); |
166 | if (!domain) |
167 | return -ENOMEM; |
168 | |
169 | irq_set_chained_handler_and_data(irq: parent_irq, handle: realtek_irq_dispatch, data: domain); |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc" , realtek_rtl_of_init); |
175 | |