Commit | Line | Data |
---|---|---|
215f4cc0 JB |
1 | /* |
2 | * Copyright (c) 2015 Endless Mobile, Inc. | |
3 | * Author: Carlo Caione <carlo@endlessm.com> | |
4 | * Copyright (c) 2016 BayLibre, SAS. | |
5 | * Author: Jerome Brunet <jbrunet@baylibre.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | * The full GNU General Public License is included in this distribution | |
19 | * in the file called COPYING. | |
20 | */ | |
21 | ||
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
23 | ||
24 | #include <linux/io.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/irq.h> | |
27 | #include <linux/irqdomain.h> | |
28 | #include <linux/irqchip.h> | |
29 | #include <linux/of.h> | |
30 | #include <linux/of_address.h> | |
31 | ||
32 | #define NUM_CHANNEL 8 | |
33 | #define MAX_INPUT_MUX 256 | |
34 | ||
35 | #define REG_EDGE_POL 0x00 | |
36 | #define REG_PIN_03_SEL 0x04 | |
37 | #define REG_PIN_47_SEL 0x08 | |
38 | #define REG_FILTER_SEL 0x0c | |
39 | ||
40 | #define REG_EDGE_POL_MASK(x) (BIT(x) | BIT(16 + (x))) | |
41 | #define REG_EDGE_POL_EDGE(x) BIT(x) | |
42 | #define REG_EDGE_POL_LOW(x) BIT(16 + (x)) | |
43 | #define REG_PIN_SEL_SHIFT(x) (((x) % 4) * 8) | |
44 | #define REG_FILTER_SEL_SHIFT(x) ((x) * 4) | |
45 | ||
46 | struct meson_gpio_irq_params { | |
47 | unsigned int nr_hwirq; | |
48 | }; | |
49 | ||
4e4cb1b1 MB |
50 | static const struct meson_gpio_irq_params meson8_params = { |
51 | .nr_hwirq = 134, | |
52 | }; | |
53 | ||
215f4cc0 JB |
54 | static const struct meson_gpio_irq_params meson8b_params = { |
55 | .nr_hwirq = 119, | |
56 | }; | |
57 | ||
58 | static const struct meson_gpio_irq_params gxbb_params = { | |
59 | .nr_hwirq = 133, | |
60 | }; | |
61 | ||
62 | static const struct meson_gpio_irq_params gxl_params = { | |
63 | .nr_hwirq = 110, | |
64 | }; | |
65 | ||
66 | static const struct of_device_id meson_irq_gpio_matches[] = { | |
4e4cb1b1 | 67 | { .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params }, |
215f4cc0 JB |
68 | { .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params }, |
69 | { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, | |
70 | { .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params }, | |
71 | { } | |
72 | }; | |
73 | ||
74 | struct meson_gpio_irq_controller { | |
75 | unsigned int nr_hwirq; | |
76 | void __iomem *base; | |
77 | u32 channel_irqs[NUM_CHANNEL]; | |
78 | DECLARE_BITMAP(channel_map, NUM_CHANNEL); | |
79 | spinlock_t lock; | |
80 | }; | |
81 | ||
82 | static void meson_gpio_irq_update_bits(struct meson_gpio_irq_controller *ctl, | |
83 | unsigned int reg, u32 mask, u32 val) | |
84 | { | |
85 | u32 tmp; | |
86 | ||
87 | tmp = readl_relaxed(ctl->base + reg); | |
88 | tmp &= ~mask; | |
89 | tmp |= val; | |
90 | writel_relaxed(tmp, ctl->base + reg); | |
91 | } | |
92 | ||
93 | static unsigned int meson_gpio_irq_channel_to_reg(unsigned int channel) | |
94 | { | |
95 | return (channel < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL; | |
96 | } | |
97 | ||
98 | static int | |
99 | meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl, | |
100 | unsigned long hwirq, | |
101 | u32 **channel_hwirq) | |
102 | { | |
103 | unsigned int reg, idx; | |
104 | ||
105 | spin_lock(&ctl->lock); | |
106 | ||
107 | /* Find a free channel */ | |
108 | idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL); | |
109 | if (idx >= NUM_CHANNEL) { | |
110 | spin_unlock(&ctl->lock); | |
111 | pr_err("No channel available\n"); | |
112 | return -ENOSPC; | |
113 | } | |
114 | ||
115 | /* Mark the channel as used */ | |
116 | set_bit(idx, ctl->channel_map); | |
117 | ||
118 | /* | |
119 | * Setup the mux of the channel to route the signal of the pad | |
120 | * to the appropriate input of the GIC | |
121 | */ | |
122 | reg = meson_gpio_irq_channel_to_reg(idx); | |
123 | meson_gpio_irq_update_bits(ctl, reg, | |
124 | 0xff << REG_PIN_SEL_SHIFT(idx), | |
125 | hwirq << REG_PIN_SEL_SHIFT(idx)); | |
126 | ||
127 | /* | |
128 | * Get the hwirq number assigned to this channel through | |
129 | * a pointer the channel_irq table. The added benifit of this | |
130 | * method is that we can also retrieve the channel index with | |
131 | * it, using the table base. | |
132 | */ | |
133 | *channel_hwirq = &(ctl->channel_irqs[idx]); | |
134 | ||
135 | spin_unlock(&ctl->lock); | |
136 | ||
137 | pr_debug("hwirq %lu assigned to channel %d - irq %u\n", | |
138 | hwirq, idx, **channel_hwirq); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static unsigned int | |
144 | meson_gpio_irq_get_channel_idx(struct meson_gpio_irq_controller *ctl, | |
145 | u32 *channel_hwirq) | |
146 | { | |
147 | return channel_hwirq - ctl->channel_irqs; | |
148 | } | |
149 | ||
150 | static void | |
151 | meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl, | |
152 | u32 *channel_hwirq) | |
153 | { | |
154 | unsigned int idx; | |
155 | ||
156 | idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); | |
157 | clear_bit(idx, ctl->channel_map); | |
158 | } | |
159 | ||
160 | static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl, | |
161 | unsigned int type, | |
162 | u32 *channel_hwirq) | |
163 | { | |
164 | u32 val = 0; | |
165 | unsigned int idx; | |
166 | ||
167 | idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); | |
168 | ||
169 | /* | |
170 | * The controller has a filter block to operate in either LEVEL or | |
171 | * EDGE mode, then signal is sent to the GIC. To enable LEVEL_LOW and | |
172 | * EDGE_FALLING support (which the GIC does not support), the filter | |
173 | * block is also able to invert the input signal it gets before | |
174 | * providing it to the GIC. | |
175 | */ | |
176 | type &= IRQ_TYPE_SENSE_MASK; | |
177 | ||
178 | if (type == IRQ_TYPE_EDGE_BOTH) | |
179 | return -EINVAL; | |
180 | ||
181 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | |
182 | val |= REG_EDGE_POL_EDGE(idx); | |
183 | ||
184 | if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) | |
185 | val |= REG_EDGE_POL_LOW(idx); | |
186 | ||
187 | spin_lock(&ctl->lock); | |
188 | ||
189 | meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, | |
190 | REG_EDGE_POL_MASK(idx), val); | |
191 | ||
192 | spin_unlock(&ctl->lock); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static unsigned int meson_gpio_irq_type_output(unsigned int type) | |
198 | { | |
199 | unsigned int sense = type & IRQ_TYPE_SENSE_MASK; | |
200 | ||
201 | type &= ~IRQ_TYPE_SENSE_MASK; | |
202 | ||
203 | /* | |
204 | * The polarity of the signal provided to the GIC should always | |
205 | * be high. | |
206 | */ | |
207 | if (sense & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) | |
208 | type |= IRQ_TYPE_LEVEL_HIGH; | |
209 | else if (sense & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | |
210 | type |= IRQ_TYPE_EDGE_RISING; | |
211 | ||
212 | return type; | |
213 | } | |
214 | ||
215 | static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type) | |
216 | { | |
217 | struct meson_gpio_irq_controller *ctl = data->domain->host_data; | |
218 | u32 *channel_hwirq = irq_data_get_irq_chip_data(data); | |
219 | int ret; | |
220 | ||
221 | ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq); | |
222 | if (ret) | |
223 | return ret; | |
224 | ||
225 | return irq_chip_set_type_parent(data, | |
226 | meson_gpio_irq_type_output(type)); | |
227 | } | |
228 | ||
229 | static struct irq_chip meson_gpio_irq_chip = { | |
230 | .name = "meson-gpio-irqchip", | |
231 | .irq_mask = irq_chip_mask_parent, | |
232 | .irq_unmask = irq_chip_unmask_parent, | |
233 | .irq_eoi = irq_chip_eoi_parent, | |
234 | .irq_set_type = meson_gpio_irq_set_type, | |
235 | .irq_retrigger = irq_chip_retrigger_hierarchy, | |
236 | #ifdef CONFIG_SMP | |
237 | .irq_set_affinity = irq_chip_set_affinity_parent, | |
238 | #endif | |
239 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
240 | }; | |
241 | ||
242 | static int meson_gpio_irq_domain_translate(struct irq_domain *domain, | |
243 | struct irq_fwspec *fwspec, | |
244 | unsigned long *hwirq, | |
245 | unsigned int *type) | |
246 | { | |
247 | if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { | |
248 | *hwirq = fwspec->param[0]; | |
249 | *type = fwspec->param[1]; | |
250 | return 0; | |
251 | } | |
252 | ||
253 | return -EINVAL; | |
254 | } | |
255 | ||
256 | static int meson_gpio_irq_allocate_gic_irq(struct irq_domain *domain, | |
257 | unsigned int virq, | |
258 | u32 hwirq, | |
259 | unsigned int type) | |
260 | { | |
261 | struct irq_fwspec fwspec; | |
262 | ||
263 | fwspec.fwnode = domain->parent->fwnode; | |
264 | fwspec.param_count = 3; | |
265 | fwspec.param[0] = 0; /* SPI */ | |
266 | fwspec.param[1] = hwirq; | |
267 | fwspec.param[2] = meson_gpio_irq_type_output(type); | |
268 | ||
269 | return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); | |
270 | } | |
271 | ||
272 | static int meson_gpio_irq_domain_alloc(struct irq_domain *domain, | |
273 | unsigned int virq, | |
274 | unsigned int nr_irqs, | |
275 | void *data) | |
276 | { | |
277 | struct irq_fwspec *fwspec = data; | |
278 | struct meson_gpio_irq_controller *ctl = domain->host_data; | |
279 | unsigned long hwirq; | |
280 | u32 *channel_hwirq; | |
281 | unsigned int type; | |
282 | int ret; | |
283 | ||
284 | if (WARN_ON(nr_irqs != 1)) | |
285 | return -EINVAL; | |
286 | ||
287 | ret = meson_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type); | |
288 | if (ret) | |
289 | return ret; | |
290 | ||
291 | ret = meson_gpio_irq_request_channel(ctl, hwirq, &channel_hwirq); | |
292 | if (ret) | |
293 | return ret; | |
294 | ||
295 | ret = meson_gpio_irq_allocate_gic_irq(domain, virq, | |
296 | *channel_hwirq, type); | |
297 | if (ret < 0) { | |
298 | pr_err("failed to allocate gic irq %u\n", *channel_hwirq); | |
299 | meson_gpio_irq_release_channel(ctl, channel_hwirq); | |
300 | return ret; | |
301 | } | |
302 | ||
303 | irq_domain_set_hwirq_and_chip(domain, virq, hwirq, | |
304 | &meson_gpio_irq_chip, channel_hwirq); | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | static void meson_gpio_irq_domain_free(struct irq_domain *domain, | |
310 | unsigned int virq, | |
311 | unsigned int nr_irqs) | |
312 | { | |
313 | struct meson_gpio_irq_controller *ctl = domain->host_data; | |
314 | struct irq_data *irq_data; | |
315 | u32 *channel_hwirq; | |
316 | ||
317 | if (WARN_ON(nr_irqs != 1)) | |
318 | return; | |
319 | ||
320 | irq_domain_free_irqs_parent(domain, virq, 1); | |
321 | ||
322 | irq_data = irq_domain_get_irq_data(domain, virq); | |
323 | channel_hwirq = irq_data_get_irq_chip_data(irq_data); | |
324 | ||
325 | meson_gpio_irq_release_channel(ctl, channel_hwirq); | |
326 | } | |
327 | ||
328 | static const struct irq_domain_ops meson_gpio_irq_domain_ops = { | |
329 | .alloc = meson_gpio_irq_domain_alloc, | |
330 | .free = meson_gpio_irq_domain_free, | |
331 | .translate = meson_gpio_irq_domain_translate, | |
332 | }; | |
333 | ||
334 | static int __init meson_gpio_irq_parse_dt(struct device_node *node, | |
335 | struct meson_gpio_irq_controller *ctl) | |
336 | { | |
337 | const struct of_device_id *match; | |
338 | const struct meson_gpio_irq_params *params; | |
339 | int ret; | |
340 | ||
341 | match = of_match_node(meson_irq_gpio_matches, node); | |
342 | if (!match) | |
343 | return -ENODEV; | |
344 | ||
345 | params = match->data; | |
346 | ctl->nr_hwirq = params->nr_hwirq; | |
347 | ||
348 | ret = of_property_read_variable_u32_array(node, | |
349 | "amlogic,channel-interrupts", | |
350 | ctl->channel_irqs, | |
351 | NUM_CHANNEL, | |
352 | NUM_CHANNEL); | |
353 | if (ret < 0) { | |
354 | pr_err("can't get %d channel interrupts\n", NUM_CHANNEL); | |
355 | return ret; | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | static int __init meson_gpio_irq_of_init(struct device_node *node, | |
362 | struct device_node *parent) | |
363 | { | |
364 | struct irq_domain *domain, *parent_domain; | |
365 | struct meson_gpio_irq_controller *ctl; | |
366 | int ret; | |
367 | ||
368 | if (!parent) { | |
369 | pr_err("missing parent interrupt node\n"); | |
370 | return -ENODEV; | |
371 | } | |
372 | ||
373 | parent_domain = irq_find_host(parent); | |
374 | if (!parent_domain) { | |
375 | pr_err("unable to obtain parent domain\n"); | |
376 | return -ENXIO; | |
377 | } | |
378 | ||
379 | ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); | |
380 | if (!ctl) | |
381 | return -ENOMEM; | |
382 | ||
383 | spin_lock_init(&ctl->lock); | |
384 | ||
385 | ctl->base = of_iomap(node, 0); | |
386 | if (!ctl->base) { | |
387 | ret = -ENOMEM; | |
388 | goto free_ctl; | |
389 | } | |
390 | ||
391 | ret = meson_gpio_irq_parse_dt(node, ctl); | |
392 | if (ret) | |
393 | goto free_channel_irqs; | |
394 | ||
395 | domain = irq_domain_create_hierarchy(parent_domain, 0, ctl->nr_hwirq, | |
396 | of_node_to_fwnode(node), | |
397 | &meson_gpio_irq_domain_ops, | |
398 | ctl); | |
399 | if (!domain) { | |
400 | pr_err("failed to add domain\n"); | |
401 | ret = -ENODEV; | |
402 | goto free_channel_irqs; | |
403 | } | |
404 | ||
405 | pr_info("%d to %d gpio interrupt mux initialized\n", | |
406 | ctl->nr_hwirq, NUM_CHANNEL); | |
407 | ||
408 | return 0; | |
409 | ||
410 | free_channel_irqs: | |
411 | iounmap(ctl->base); | |
412 | free_ctl: | |
413 | kfree(ctl); | |
414 | ||
415 | return ret; | |
416 | } | |
417 | ||
418 | IRQCHIP_DECLARE(meson_gpio_intc, "amlogic,meson-gpio-intc", | |
419 | meson_gpio_irq_of_init); |