1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2015-2016 Vladimir Zapolskiy <vz@mleia.com> |
4 | */ |
5 | |
6 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/irqchip.h> |
10 | #include <linux/irqchip/chained_irq.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_irq.h> |
13 | #include <linux/of_platform.h> |
14 | #include <linux/seq_file.h> |
15 | #include <linux/slab.h> |
16 | #include <asm/exception.h> |
17 | |
18 | #define LPC32XX_INTC_MASK 0x00 |
19 | #define LPC32XX_INTC_RAW 0x04 |
20 | #define LPC32XX_INTC_STAT 0x08 |
21 | #define LPC32XX_INTC_POL 0x0C |
22 | #define LPC32XX_INTC_TYPE 0x10 |
23 | #define LPC32XX_INTC_FIQ 0x14 |
24 | |
25 | #define NR_LPC32XX_IC_IRQS 32 |
26 | |
27 | struct lpc32xx_irq_chip { |
28 | void __iomem *base; |
29 | phys_addr_t addr; |
30 | struct irq_domain *domain; |
31 | }; |
32 | |
33 | static struct lpc32xx_irq_chip *lpc32xx_mic_irqc; |
34 | |
35 | static inline u32 lpc32xx_ic_read(struct lpc32xx_irq_chip *ic, u32 reg) |
36 | { |
37 | return readl_relaxed(ic->base + reg); |
38 | } |
39 | |
40 | static inline void lpc32xx_ic_write(struct lpc32xx_irq_chip *ic, |
41 | u32 reg, u32 val) |
42 | { |
43 | writel_relaxed(val, ic->base + reg); |
44 | } |
45 | |
46 | static void lpc32xx_irq_mask(struct irq_data *d) |
47 | { |
48 | struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); |
49 | u32 val, mask = BIT(d->hwirq); |
50 | |
51 | val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) & ~mask; |
52 | lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val); |
53 | } |
54 | |
55 | static void lpc32xx_irq_unmask(struct irq_data *d) |
56 | { |
57 | struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); |
58 | u32 val, mask = BIT(d->hwirq); |
59 | |
60 | val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) | mask; |
61 | lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val); |
62 | } |
63 | |
64 | static void lpc32xx_irq_ack(struct irq_data *d) |
65 | { |
66 | struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); |
67 | u32 mask = BIT(d->hwirq); |
68 | |
69 | lpc32xx_ic_write(ic, LPC32XX_INTC_RAW, val: mask); |
70 | } |
71 | |
72 | static int lpc32xx_irq_set_type(struct irq_data *d, unsigned int type) |
73 | { |
74 | struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); |
75 | u32 val, mask = BIT(d->hwirq); |
76 | bool high, edge; |
77 | |
78 | switch (type) { |
79 | case IRQ_TYPE_EDGE_RISING: |
80 | edge = true; |
81 | high = true; |
82 | break; |
83 | case IRQ_TYPE_EDGE_FALLING: |
84 | edge = true; |
85 | high = false; |
86 | break; |
87 | case IRQ_TYPE_LEVEL_HIGH: |
88 | edge = false; |
89 | high = true; |
90 | break; |
91 | case IRQ_TYPE_LEVEL_LOW: |
92 | edge = false; |
93 | high = false; |
94 | break; |
95 | default: |
96 | pr_info("unsupported irq type %d\n" , type); |
97 | return -EINVAL; |
98 | } |
99 | |
100 | irqd_set_trigger_type(d, type); |
101 | |
102 | val = lpc32xx_ic_read(ic, LPC32XX_INTC_POL); |
103 | if (high) |
104 | val |= mask; |
105 | else |
106 | val &= ~mask; |
107 | lpc32xx_ic_write(ic, LPC32XX_INTC_POL, val); |
108 | |
109 | val = lpc32xx_ic_read(ic, LPC32XX_INTC_TYPE); |
110 | if (edge) { |
111 | val |= mask; |
112 | irq_set_handler_locked(data: d, handler: handle_edge_irq); |
113 | } else { |
114 | val &= ~mask; |
115 | irq_set_handler_locked(data: d, handler: handle_level_irq); |
116 | } |
117 | lpc32xx_ic_write(ic, LPC32XX_INTC_TYPE, val); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static void lpc32xx_irq_print_chip(struct irq_data *d, struct seq_file *p) |
123 | { |
124 | struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d); |
125 | |
126 | if (ic == lpc32xx_mic_irqc) |
127 | seq_printf(m: p, fmt: "%08x.mic" , ic->addr); |
128 | else |
129 | seq_printf(m: p, fmt: "%08x.sic" , ic->addr); |
130 | } |
131 | |
132 | static const struct irq_chip lpc32xx_chip = { |
133 | .irq_ack = lpc32xx_irq_ack, |
134 | .irq_mask = lpc32xx_irq_mask, |
135 | .irq_unmask = lpc32xx_irq_unmask, |
136 | .irq_set_type = lpc32xx_irq_set_type, |
137 | .irq_print_chip = lpc32xx_irq_print_chip, |
138 | }; |
139 | |
140 | static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs) |
141 | { |
142 | struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc; |
143 | u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq; |
144 | |
145 | while (hwirq) { |
146 | irq = __ffs(hwirq); |
147 | hwirq &= ~BIT(irq); |
148 | generic_handle_domain_irq(lpc32xx_mic_irqc->domain, irq); |
149 | } |
150 | } |
151 | |
152 | static void lpc32xx_sic_handler(struct irq_desc *desc) |
153 | { |
154 | struct lpc32xx_irq_chip *ic = irq_desc_get_handler_data(desc); |
155 | struct irq_chip *chip = irq_desc_get_chip(desc); |
156 | u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq; |
157 | |
158 | chained_irq_enter(chip, desc); |
159 | |
160 | while (hwirq) { |
161 | irq = __ffs(hwirq); |
162 | hwirq &= ~BIT(irq); |
163 | generic_handle_domain_irq(domain: ic->domain, hwirq: irq); |
164 | } |
165 | |
166 | chained_irq_exit(chip, desc); |
167 | } |
168 | |
169 | static int lpc32xx_irq_domain_map(struct irq_domain *id, unsigned int virq, |
170 | irq_hw_number_t hw) |
171 | { |
172 | struct lpc32xx_irq_chip *ic = id->host_data; |
173 | |
174 | irq_set_chip_data(irq: virq, data: ic); |
175 | irq_set_chip_and_handler(irq: virq, chip: &lpc32xx_chip, handle: handle_level_irq); |
176 | irq_set_status_flags(irq: virq, set: IRQ_LEVEL); |
177 | irq_set_noprobe(irq: virq); |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static void lpc32xx_irq_domain_unmap(struct irq_domain *id, unsigned int virq) |
183 | { |
184 | irq_set_chip_and_handler(irq: virq, NULL, NULL); |
185 | } |
186 | |
187 | static const struct irq_domain_ops lpc32xx_irq_domain_ops = { |
188 | .map = lpc32xx_irq_domain_map, |
189 | .unmap = lpc32xx_irq_domain_unmap, |
190 | .xlate = irq_domain_xlate_twocell, |
191 | }; |
192 | |
193 | static int __init lpc32xx_of_ic_init(struct device_node *node, |
194 | struct device_node *parent) |
195 | { |
196 | struct lpc32xx_irq_chip *irqc; |
197 | bool is_mic = of_device_is_compatible(device: node, "nxp,lpc3220-mic" ); |
198 | const __be32 *reg = of_get_property(node, name: "reg" , NULL); |
199 | u32 parent_irq, i, addr = reg ? be32_to_cpu(*reg) : 0; |
200 | |
201 | irqc = kzalloc(size: sizeof(*irqc), GFP_KERNEL); |
202 | if (!irqc) |
203 | return -ENOMEM; |
204 | |
205 | irqc->addr = addr; |
206 | irqc->base = of_iomap(node, index: 0); |
207 | if (!irqc->base) { |
208 | pr_err("%pOF: unable to map registers\n" , node); |
209 | kfree(objp: irqc); |
210 | return -EINVAL; |
211 | } |
212 | |
213 | irqc->domain = irq_domain_add_linear(of_node: node, NR_LPC32XX_IC_IRQS, |
214 | ops: &lpc32xx_irq_domain_ops, host_data: irqc); |
215 | if (!irqc->domain) { |
216 | pr_err("unable to add irq domain\n" ); |
217 | iounmap(addr: irqc->base); |
218 | kfree(objp: irqc); |
219 | return -ENODEV; |
220 | } |
221 | |
222 | if (is_mic) { |
223 | lpc32xx_mic_irqc = irqc; |
224 | set_handle_irq(lpc32xx_handle_irq); |
225 | } else { |
226 | for (i = 0; i < of_irq_count(dev: node); i++) { |
227 | parent_irq = irq_of_parse_and_map(node, index: i); |
228 | if (parent_irq) |
229 | irq_set_chained_handler_and_data(irq: parent_irq, |
230 | handle: lpc32xx_sic_handler, data: irqc); |
231 | } |
232 | } |
233 | |
234 | lpc32xx_ic_write(ic: irqc, LPC32XX_INTC_MASK, val: 0x00); |
235 | lpc32xx_ic_write(ic: irqc, LPC32XX_INTC_POL, val: 0x00); |
236 | lpc32xx_ic_write(ic: irqc, LPC32XX_INTC_TYPE, val: 0x00); |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | IRQCHIP_DECLARE(nxp_lpc32xx_mic, "nxp,lpc3220-mic" , lpc32xx_of_ic_init); |
242 | IRQCHIP_DECLARE(nxp_lpc32xx_sic, "nxp,lpc3220-sic" , lpc32xx_of_ic_init); |
243 | |