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/bits.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irqdomain.h> |
18 | #include <linux/irq.h> |
19 | #include <linux/irqchip.h> |
20 | #include <linux/irqchip/xtensa-pic.h> |
21 | #include <linux/of.h> |
22 | |
23 | /* |
24 | * Device Tree IRQ specifier translation function which works with one or |
25 | * two cell bindings. First cell value maps directly to the hwirq number. |
26 | * Second cell if present specifies whether hwirq number is external (1) or |
27 | * internal (0). |
28 | */ |
29 | static int xtensa_pic_irq_domain_xlate(struct irq_domain *d, |
30 | struct device_node *ctrlr, |
31 | const u32 *intspec, unsigned int intsize, |
32 | unsigned long *out_hwirq, unsigned int *out_type) |
33 | { |
34 | return xtensa_irq_domain_xlate(intspec, intsize, |
35 | intspec[0], intspec[0], |
36 | out_hwirq, out_type); |
37 | } |
38 | |
39 | static const struct irq_domain_ops xtensa_irq_domain_ops = { |
40 | .xlate = xtensa_pic_irq_domain_xlate, |
41 | .map = xtensa_irq_map, |
42 | }; |
43 | |
44 | static void xtensa_irq_mask(struct irq_data *d) |
45 | { |
46 | u32 irq_mask; |
47 | |
48 | irq_mask = xtensa_get_sr(intenable); |
49 | irq_mask &= ~BIT(d->hwirq); |
50 | xtensa_set_sr(irq_mask, intenable); |
51 | } |
52 | |
53 | static void xtensa_irq_unmask(struct irq_data *d) |
54 | { |
55 | u32 irq_mask; |
56 | |
57 | irq_mask = xtensa_get_sr(intenable); |
58 | irq_mask |= BIT(d->hwirq); |
59 | xtensa_set_sr(irq_mask, intenable); |
60 | } |
61 | |
62 | static void xtensa_irq_ack(struct irq_data *d) |
63 | { |
64 | xtensa_set_sr(BIT(d->hwirq), intclear); |
65 | } |
66 | |
67 | static int xtensa_irq_retrigger(struct irq_data *d) |
68 | { |
69 | unsigned int mask = BIT(d->hwirq); |
70 | |
71 | if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE)) |
72 | return 0; |
73 | xtensa_set_sr(mask, intset); |
74 | return 1; |
75 | } |
76 | |
77 | static struct irq_chip xtensa_irq_chip = { |
78 | .name = "xtensa" , |
79 | .irq_mask = xtensa_irq_mask, |
80 | .irq_unmask = xtensa_irq_unmask, |
81 | .irq_ack = xtensa_irq_ack, |
82 | .irq_retrigger = xtensa_irq_retrigger, |
83 | }; |
84 | |
85 | int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) |
86 | { |
87 | struct irq_domain *root_domain = |
88 | irq_domain_add_legacy(NULL, NR_IRQS - 1, first_irq: 1, first_hwirq: 0, |
89 | ops: &xtensa_irq_domain_ops, host_data: &xtensa_irq_chip); |
90 | irq_set_default_host(host: root_domain); |
91 | return 0; |
92 | } |
93 | |
94 | static int __init xtensa_pic_init(struct device_node *np, |
95 | struct device_node *interrupt_parent) |
96 | { |
97 | struct irq_domain *root_domain = |
98 | irq_domain_add_linear(of_node: np, NR_IRQS, ops: &xtensa_irq_domain_ops, |
99 | host_data: &xtensa_irq_chip); |
100 | irq_set_default_host(host: root_domain); |
101 | return 0; |
102 | } |
103 | IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic" , xtensa_pic_init); |
104 | |