1 | /* |
2 | * Xtensa built-in interrupt controller |
3 | * |
4 | * Copyright (C) 2002 - 2013 Tensilica, Inc. |
5 | * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar |
6 | * |
7 | * This file is subject to the terms and conditions of the GNU General Public |
8 | * License. See the file "COPYING" in the main directory of this archive |
9 | * for more details. |
10 | * |
11 | * Chris Zankel <chris@zankel.net> |
12 | * Kevin Chea |
13 | */ |
14 | |
15 | #include <linux/interrupt.h> |
16 | #include <linux/irqdomain.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/irqchip.h> |
19 | #include <linux/irqchip/xtensa-pic.h> |
20 | #include <linux/of.h> |
21 | |
22 | unsigned int cached_irq_mask; |
23 | |
24 | /* |
25 | * Device Tree IRQ specifier translation function which works with one or |
26 | * two cell bindings. First cell value maps directly to the hwirq number. |
27 | * Second cell if present specifies whether hwirq number is external (1) or |
28 | * internal (0). |
29 | */ |
30 | static int xtensa_pic_irq_domain_xlate(struct irq_domain *d, |
31 | struct device_node *ctrlr, |
32 | const u32 *intspec, unsigned int intsize, |
33 | unsigned long *out_hwirq, unsigned int *out_type) |
34 | { |
35 | return xtensa_irq_domain_xlate(intspec, intsize, |
36 | intspec[0], intspec[0], |
37 | out_hwirq, out_type); |
38 | } |
39 | |
40 | static const struct irq_domain_ops xtensa_irq_domain_ops = { |
41 | .xlate = xtensa_pic_irq_domain_xlate, |
42 | .map = xtensa_irq_map, |
43 | }; |
44 | |
45 | static void xtensa_irq_mask(struct irq_data *d) |
46 | { |
47 | cached_irq_mask &= ~(1 << d->hwirq); |
48 | xtensa_set_sr(cached_irq_mask, intenable); |
49 | } |
50 | |
51 | static void xtensa_irq_unmask(struct irq_data *d) |
52 | { |
53 | cached_irq_mask |= 1 << d->hwirq; |
54 | xtensa_set_sr(cached_irq_mask, intenable); |
55 | } |
56 | |
57 | static void xtensa_irq_enable(struct irq_data *d) |
58 | { |
59 | xtensa_irq_unmask(d); |
60 | } |
61 | |
62 | static void xtensa_irq_disable(struct irq_data *d) |
63 | { |
64 | xtensa_irq_mask(d); |
65 | } |
66 | |
67 | static void xtensa_irq_ack(struct irq_data *d) |
68 | { |
69 | xtensa_set_sr(1 << d->hwirq, intclear); |
70 | } |
71 | |
72 | static int xtensa_irq_retrigger(struct irq_data *d) |
73 | { |
74 | unsigned int mask = 1u << d->hwirq; |
75 | |
76 | if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE)) |
77 | return 0; |
78 | xtensa_set_sr(mask, intset); |
79 | return 1; |
80 | } |
81 | |
82 | static struct irq_chip xtensa_irq_chip = { |
83 | .name = "xtensa" , |
84 | .irq_enable = xtensa_irq_enable, |
85 | .irq_disable = xtensa_irq_disable, |
86 | .irq_mask = xtensa_irq_mask, |
87 | .irq_unmask = xtensa_irq_unmask, |
88 | .irq_ack = xtensa_irq_ack, |
89 | .irq_retrigger = xtensa_irq_retrigger, |
90 | }; |
91 | |
92 | int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) |
93 | { |
94 | struct irq_domain *root_domain = |
95 | irq_domain_add_legacy(NULL, NR_IRQS - 1, first_irq: 1, first_hwirq: 0, |
96 | ops: &xtensa_irq_domain_ops, host_data: &xtensa_irq_chip); |
97 | irq_set_default_host(host: root_domain); |
98 | return 0; |
99 | } |
100 | |
101 | static int __init xtensa_pic_init(struct device_node *np, |
102 | struct device_node *interrupt_parent) |
103 | { |
104 | struct irq_domain *root_domain = |
105 | irq_domain_add_linear(of_node: np, NR_IRQS, ops: &xtensa_irq_domain_ops, |
106 | host_data: &xtensa_irq_chip); |
107 | irq_set_default_host(host: root_domain); |
108 | return 0; |
109 | } |
110 | IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic" , xtensa_pic_init); |
111 | |