Commit cbd1de2e8e46207cead11034f92ea8a018b11189

Authored by Max Filippov
Committed by Chris Zankel
1 parent c8f3a7dc01

xtensa: move built-in PIC to drivers/irqchip

Extract xtensa built-in interrupt controller implementation from
xtensa/kernel/irq.c and move it to other irqchips, providing way to
instantiate it from the device tree.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>

Showing 6 changed files with 165 additions and 108 deletions Side-by-side Diff

arch/xtensa/boot/dts/xtfpga.dtsi
... ... @@ -26,7 +26,7 @@
26 26 };
27 27  
28 28 pic: pic {
29   - compatible = "xtensa,pic";
  29 + compatible = "cdns,xtensa-pic";
30 30 /* one cell: internal irq number,
31 31 * two cells: second cell == 0: internal irq number
32 32 * second cell == 1: external irq number
arch/xtensa/include/asm/irq.h
... ... @@ -43,6 +43,13 @@
43 43 }
44 44  
45 45 struct irqaction;
  46 +struct irq_domain;
  47 +
  48 +int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
  49 + unsigned long int_irq, unsigned long ext_irq,
  50 + unsigned long *out_hwirq, unsigned int *out_type);
  51 +int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw);
  52 +unsigned xtensa_map_ext_irq(unsigned ext_irq);
46 53  
47 54 #endif /* _XTENSA_IRQ_H */
arch/xtensa/kernel/irq.c
... ... @@ -18,28 +18,20 @@
18 18 #include <linux/interrupt.h>
19 19 #include <linux/irq.h>
20 20 #include <linux/kernel_stat.h>
  21 +#include <linux/irqchip.h>
  22 +#include <linux/irqchip/xtensa-pic.h>
21 23 #include <linux/irqdomain.h>
22 24 #include <linux/of.h>
23 25  
24 26 #include <asm/uaccess.h>
25 27 #include <asm/platform.h>
26 28  
27   -static unsigned int cached_irq_mask;
28   -
29 29 atomic_t irq_err_count;
30 30  
31   -static struct irq_domain *root_domain;
32   -
33   -/*
34   - * do_IRQ handles all normal device IRQ's (the special
35   - * SMP cross-CPU interrupts have their own specific
36   - * handlers).
37   - */
38   -
39 31 asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
40 32 {
41 33 struct pt_regs *old_regs = set_irq_regs(regs);
42   - int irq = irq_find_mapping(root_domain, hwirq);
  34 + int irq = irq_find_mapping(NULL, hwirq);
43 35  
44 36 if (hwirq >= NR_IRQS) {
45 37 printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
46 38  
47 39  
48 40  
49 41  
50 42  
51 43  
52 44  
53 45  
54 46  
... ... @@ -74,83 +66,57 @@
74 66 return 0;
75 67 }
76 68  
77   -static void xtensa_irq_mask(struct irq_data *d)
  69 +int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
  70 + unsigned long int_irq, unsigned long ext_irq,
  71 + unsigned long *out_hwirq, unsigned int *out_type)
78 72 {
79   - cached_irq_mask &= ~(1 << d->hwirq);
80   - set_sr (cached_irq_mask, intenable);
  73 + if (WARN_ON(intsize < 1 || intsize > 2))
  74 + return -EINVAL;
  75 + if (intsize == 2 && intspec[1] == 1) {
  76 + int_irq = xtensa_map_ext_irq(ext_irq);
  77 + if (int_irq < XCHAL_NUM_INTERRUPTS)
  78 + *out_hwirq = int_irq;
  79 + else
  80 + return -EINVAL;
  81 + } else {
  82 + *out_hwirq = int_irq;
  83 + }
  84 + *out_type = IRQ_TYPE_NONE;
  85 + return 0;
81 86 }
82 87  
83   -static void xtensa_irq_unmask(struct irq_data *d)
84   -{
85   - cached_irq_mask |= 1 << d->hwirq;
86   - set_sr (cached_irq_mask, intenable);
87   -}
88   -
89   -static void xtensa_irq_enable(struct irq_data *d)
90   -{
91   - variant_irq_enable(d->hwirq);
92   - xtensa_irq_unmask(d);
93   -}
94   -
95   -static void xtensa_irq_disable(struct irq_data *d)
96   -{
97   - xtensa_irq_mask(d);
98   - variant_irq_disable(d->hwirq);
99   -}
100   -
101   -static void xtensa_irq_ack(struct irq_data *d)
102   -{
103   - set_sr(1 << d->hwirq, intclear);
104   -}
105   -
106   -static int xtensa_irq_retrigger(struct irq_data *d)
107   -{
108   - set_sr(1 << d->hwirq, intset);
109   - return 1;
110   -}
111   -
112   -static struct irq_chip xtensa_irq_chip = {
113   - .name = "xtensa",
114   - .irq_enable = xtensa_irq_enable,
115   - .irq_disable = xtensa_irq_disable,
116   - .irq_mask = xtensa_irq_mask,
117   - .irq_unmask = xtensa_irq_unmask,
118   - .irq_ack = xtensa_irq_ack,
119   - .irq_retrigger = xtensa_irq_retrigger,
120   -};
121   -
122   -static int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
  88 +int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
123 89 irq_hw_number_t hw)
124 90 {
  91 + struct irq_chip *irq_chip = d->host_data;
125 92 u32 mask = 1 << hw;
126 93  
127 94 if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) {
128   - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
  95 + irq_set_chip_and_handler_name(irq, irq_chip,
129 96 handle_simple_irq, "level");
130 97 irq_set_status_flags(irq, IRQ_LEVEL);
131 98 } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) {
132   - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
  99 + irq_set_chip_and_handler_name(irq, irq_chip,
133 100 handle_edge_irq, "edge");
134 101 irq_clear_status_flags(irq, IRQ_LEVEL);
135 102 } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) {
136   - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
  103 + irq_set_chip_and_handler_name(irq, irq_chip,
137 104 handle_level_irq, "level");
138 105 irq_set_status_flags(irq, IRQ_LEVEL);
139 106 } else if (mask & XCHAL_INTTYPE_MASK_TIMER) {
140   - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
141   - handle_edge_irq, "edge");
  107 + irq_set_chip_and_handler_name(irq, irq_chip,
  108 + handle_percpu_irq, "timer");
142 109 irq_clear_status_flags(irq, IRQ_LEVEL);
143 110 } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */
144 111 /* XCHAL_INTTYPE_MASK_NMI */
145   -
146   - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
  112 + irq_set_chip_and_handler_name(irq, irq_chip,
147 113 handle_level_irq, "level");
148 114 irq_set_status_flags(irq, IRQ_LEVEL);
149 115 }
150 116 return 0;
151 117 }
152 118  
153   -static unsigned map_ext_irq(unsigned ext_irq)
  119 +unsigned xtensa_map_ext_irq(unsigned ext_irq)
154 120 {
155 121 unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE |
156 122 XCHAL_INTTYPE_MASK_EXTERN_LEVEL;
157 123  
158 124  
159 125  
160 126  
... ... @@ -163,56 +129,13 @@
163 129 return XCHAL_NUM_INTERRUPTS;
164 130 }
165 131  
166   -/*
167   - * Device Tree IRQ specifier translation function which works with one or
168   - * two cell bindings. First cell value maps directly to the hwirq number.
169   - * Second cell if present specifies whether hwirq number is external (1) or
170   - * internal (0).
171   - */
172   -int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
173   - const u32 *intspec, unsigned int intsize,
174   - unsigned long *out_hwirq, unsigned int *out_type)
175   -{
176   - if (WARN_ON(intsize < 1 || intsize > 2))
177   - return -EINVAL;
178   - if (intsize == 2 && intspec[1] == 1) {
179   - unsigned int_irq = map_ext_irq(intspec[0]);
180   - if (int_irq < XCHAL_NUM_INTERRUPTS)
181   - *out_hwirq = int_irq;
182   - else
183   - return -EINVAL;
184   - } else {
185   - *out_hwirq = intspec[0];
186   - }
187   - *out_type = IRQ_TYPE_NONE;
188   - return 0;
189   -}
190   -
191   -static const struct irq_domain_ops xtensa_irq_domain_ops = {
192   - .xlate = xtensa_irq_domain_xlate,
193   - .map = xtensa_irq_map,
194   -};
195   -
196 132 void __init init_IRQ(void)
197 133 {
198   - struct device_node *intc = NULL;
199   -
200   - cached_irq_mask = 0;
201   - set_sr(~0, intclear);
202   -
203 134 #ifdef CONFIG_OF
204   - /* The interrupt controller device node is mandatory */
205   - intc = of_find_compatible_node(NULL, NULL, "xtensa,pic");
206   - BUG_ON(!intc);
207   -
208   - root_domain = irq_domain_add_linear(intc, NR_IRQS,
209   - &xtensa_irq_domain_ops, NULL);
  135 + irqchip_init();
210 136 #else
211   - root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0,
212   - &xtensa_irq_domain_ops, NULL);
  137 + xtensa_pic_init_legacy(NULL);
213 138 #endif
214   - irq_set_default_host(root_domain);
215   -
216 139 variant_init_irq();
217 140 }
drivers/irqchip/Makefile
... ... @@ -22,4 +22,5 @@
22 22 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
23 23 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
24 24 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
  25 +obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
drivers/irqchip/irq-xtensa-pic.c
  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/of.h>
  19 +
  20 +#include "irqchip.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 + 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 + set_sr(cached_irq_mask, intenable);
  55 +}
  56 +
  57 +static void xtensa_irq_enable(struct irq_data *d)
  58 +{
  59 + variant_irq_enable(d->hwirq);
  60 + xtensa_irq_unmask(d);
  61 +}
  62 +
  63 +static void xtensa_irq_disable(struct irq_data *d)
  64 +{
  65 + xtensa_irq_mask(d);
  66 + variant_irq_disable(d->hwirq);
  67 +}
  68 +
  69 +static void xtensa_irq_ack(struct irq_data *d)
  70 +{
  71 + set_sr(1 << d->hwirq, intclear);
  72 +}
  73 +
  74 +static int xtensa_irq_retrigger(struct irq_data *d)
  75 +{
  76 + set_sr(1 << d->hwirq, intset);
  77 + return 1;
  78 +}
  79 +
  80 +static struct irq_chip xtensa_irq_chip = {
  81 + .name = "xtensa",
  82 + .irq_enable = xtensa_irq_enable,
  83 + .irq_disable = xtensa_irq_disable,
  84 + .irq_mask = xtensa_irq_mask,
  85 + .irq_unmask = xtensa_irq_unmask,
  86 + .irq_ack = xtensa_irq_ack,
  87 + .irq_retrigger = xtensa_irq_retrigger,
  88 +};
  89 +
  90 +int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent)
  91 +{
  92 + struct irq_domain *root_domain =
  93 + irq_domain_add_legacy(NULL, NR_IRQS, 0, 0,
  94 + &xtensa_irq_domain_ops, &xtensa_irq_chip);
  95 + irq_set_default_host(root_domain);
  96 + return 0;
  97 +}
  98 +
  99 +static int __init xtensa_pic_init(struct device_node *np,
  100 + struct device_node *interrupt_parent)
  101 +{
  102 + struct irq_domain *root_domain =
  103 + irq_domain_add_linear(np, NR_IRQS, &xtensa_irq_domain_ops,
  104 + &xtensa_irq_chip);
  105 + irq_set_default_host(root_domain);
  106 + return 0;
  107 +}
  108 +IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic", xtensa_pic_init);
include/linux/irqchip/xtensa-pic.h
  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 +
  12 +#ifndef __LINUX_IRQCHIP_XTENSA_PIC_H
  13 +#define __LINUX_IRQCHIP_XTENSA_PIC_H
  14 +
  15 +struct device_node;
  16 +int xtensa_pic_init_legacy(struct device_node *interrupt_parent);
  17 +
  18 +#endif /* __LINUX_IRQCHIP_XTENSA_PIC_H */