summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-meson-gpio.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-13 20:33:11 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-13 20:33:11 -0500
commit670310dfbae0eefe7318ff6a61e29e67a7a7bbce (patch)
treeeb3ce3aa3e6786a64fec93d410bb6f0b9a56be77 /drivers/irqchip/irq-meson-gpio.c
parent43ff2f4db9d0f76452b77cfa645f02b471143b24 (diff)
parentffc661c99f621152d5fdcf53f9df0d48c326318b (diff)
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq core updates from Thomas Gleixner: "A rather large update for the interrupt core code and the irq chip drivers: - Add a new bitmap matrix allocator and supporting changes, which is used to replace the x86 vector allocator which comes with separate pull request. This allows to replace the convoluted nested loop allocation function in x86 with a facility which supports the recently added property of managed interrupts proper and allows to switch to a best effort vector reservation scheme, which addresses problems with vector exhaustion. - A large update to the ARM GIC-V3-ITS driver adding support for range selectors. - New interrupt controllers: - Meson and Meson8 GPIO - BCM7271 L2 - Socionext EXIU If you expected that this will stop at some point, I have to disappoint you. There are new ones posted already. Sigh! - STM32 interrupt controller support for new platforms. - A pile of fixes, cleanups and updates to the MIPS GIC driver - The usual small fixes, cleanups and updates all over the place. Most visible one is to move the irq chip drivers Kconfig switches into a separate Kconfig menu" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (70 commits) genirq: Fix type of shifting literal 1 in __setup_irq() irqdomain: Drop pointless NULL check in virq_debug_show_one genirq/proc: Return proper error code when irq_set_affinity() fails irq/work: Use llist_for_each_entry_safe irqchip: mips-gic: Print warning if inherited GIC base is used irqchip/mips-gic: Add pr_fmt and reword pr_* messages irqchip/stm32: Move the wakeup on interrupt mask irqchip/stm32: Fix initial values irqchip/stm32: Add stm32h7 support dt-bindings/interrupt-controllers: Add compatible string for stm32h7 irqchip/stm32: Add multi-bank management irqchip/stm32: Select GENERIC_IRQ_CHIP irqchip/exiu: Add support for Socionext Synquacer EXIU controller dt-bindings: Add description of Socionext EXIU interrupt controller irqchip/gic-v3-its: Fix VPE activate callback return value irqchip: mips-gic: Make IPI bitmaps static irqchip: mips-gic: Share register writes in gic_set_type() irqchip: mips-gic: Remove gic_vpes variable irqchip: mips-gic: Use num_possible_cpus() to reserve IPIs irqchip: mips-gic: Configure EIC when CPUs come online ...
Diffstat (limited to 'drivers/irqchip/irq-meson-gpio.c')
-rw-r--r--drivers/irqchip/irq-meson-gpio.c419
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c
new file mode 100644
index 000000000000..a59bdbc0b9bb
--- /dev/null
+++ b/drivers/irqchip/irq-meson-gpio.c
@@ -0,0 +1,419 @@
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
46struct meson_gpio_irq_params {
47 unsigned int nr_hwirq;
48};
49
50static const struct meson_gpio_irq_params meson8_params = {
51 .nr_hwirq = 134,
52};
53
54static const struct meson_gpio_irq_params meson8b_params = {
55 .nr_hwirq = 119,
56};
57
58static const struct meson_gpio_irq_params gxbb_params = {
59 .nr_hwirq = 133,
60};
61
62static const struct meson_gpio_irq_params gxl_params = {
63 .nr_hwirq = 110,
64};
65
66static const struct of_device_id meson_irq_gpio_matches[] = {
67 { .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
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
74struct 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
82static 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
93static 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
98static int
99meson_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
143static unsigned int
144meson_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
150static void
151meson_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
160static 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
197static 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
215static 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
229static 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
242static 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
256static 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
272static 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
309static 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
328static 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
334static 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
361static 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
410free_channel_irqs:
411 iounmap(ctl->base);
412free_ctl:
413 kfree(ctl);
414
415 return ret;
416}
417
418IRQCHIP_DECLARE(meson_gpio_intc, "amlogic,meson-gpio-intc",
419 meson_gpio_irq_of_init);