diff options
author | Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com> | 2016-11-14 07:13:51 -0500 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2016-11-29 04:14:50 -0500 |
commit | 8328255ff81ed422847b443f81b689366e98ce95 (patch) | |
tree | 0c8ac90f545a9c44c5199aa605e5b65d78bf07dd /arch/powerpc/sysdev/xilinx_intc.c | |
parent | 8a11da598e2ff96050a99e24772e1568e3e31c4d (diff) |
powerpc/virtex: Use generic xilinx irqchip driver
The Xilinx interrupt controller driver is now available in drivers/irqchip.
Switch to using that driver.
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Acked-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'arch/powerpc/sysdev/xilinx_intc.c')
-rw-r--r-- | arch/powerpc/sysdev/xilinx_intc.c | 211 |
1 files changed, 2 insertions, 209 deletions
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index 0f52d7955796..4a86dcff3fcd 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c | |||
@@ -29,194 +29,7 @@ | |||
29 | #include <asm/processor.h> | 29 | #include <asm/processor.h> |
30 | #include <asm/i8259.h> | 30 | #include <asm/i8259.h> |
31 | #include <asm/irq.h> | 31 | #include <asm/irq.h> |
32 | 32 | #include <linux/irqchip.h> | |
33 | /* | ||
34 | * INTC Registers | ||
35 | */ | ||
36 | #define XINTC_ISR 0 /* Interrupt Status */ | ||
37 | #define XINTC_IPR 4 /* Interrupt Pending */ | ||
38 | #define XINTC_IER 8 /* Interrupt Enable */ | ||
39 | #define XINTC_IAR 12 /* Interrupt Acknowledge */ | ||
40 | #define XINTC_SIE 16 /* Set Interrupt Enable bits */ | ||
41 | #define XINTC_CIE 20 /* Clear Interrupt Enable bits */ | ||
42 | #define XINTC_IVR 24 /* Interrupt Vector */ | ||
43 | #define XINTC_MER 28 /* Master Enable */ | ||
44 | |||
45 | static struct irq_domain *master_irqhost; | ||
46 | |||
47 | #define XILINX_INTC_MAXIRQS (32) | ||
48 | |||
49 | /* The following table allows the interrupt type, edge or level, | ||
50 | * to be cached after being read from the device tree until the interrupt | ||
51 | * is mapped | ||
52 | */ | ||
53 | static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS]; | ||
54 | |||
55 | /* Map the interrupt type from the device tree to the interrupt types | ||
56 | * used by the interrupt subsystem | ||
57 | */ | ||
58 | static unsigned char xilinx_intc_map_senses[] = { | ||
59 | IRQ_TYPE_EDGE_RISING, | ||
60 | IRQ_TYPE_EDGE_FALLING, | ||
61 | IRQ_TYPE_LEVEL_HIGH, | ||
62 | IRQ_TYPE_LEVEL_LOW, | ||
63 | }; | ||
64 | |||
65 | /* | ||
66 | * The interrupt controller is setup such that it doesn't work well with | ||
67 | * the level interrupt handler in the kernel because the handler acks the | ||
68 | * interrupt before calling the application interrupt handler. To deal with | ||
69 | * that, we use 2 different irq chips so that different functions can be | ||
70 | * used for level and edge type interrupts. | ||
71 | * | ||
72 | * IRQ Chip common (across level and edge) operations | ||
73 | */ | ||
74 | static void xilinx_intc_mask(struct irq_data *d) | ||
75 | { | ||
76 | int irq = irqd_to_hwirq(d); | ||
77 | void * regs = irq_data_get_irq_chip_data(d); | ||
78 | pr_debug("mask: %d\n", irq); | ||
79 | out_be32(regs + XINTC_CIE, 1 << irq); | ||
80 | } | ||
81 | |||
82 | static int xilinx_intc_set_type(struct irq_data *d, unsigned int flow_type) | ||
83 | { | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * IRQ Chip level operations | ||
89 | */ | ||
90 | static void xilinx_intc_level_unmask(struct irq_data *d) | ||
91 | { | ||
92 | int irq = irqd_to_hwirq(d); | ||
93 | void * regs = irq_data_get_irq_chip_data(d); | ||
94 | pr_debug("unmask: %d\n", irq); | ||
95 | out_be32(regs + XINTC_SIE, 1 << irq); | ||
96 | |||
97 | /* ack level irqs because they can't be acked during | ||
98 | * ack function since the handle_level_irq function | ||
99 | * acks the irq before calling the inerrupt handler | ||
100 | */ | ||
101 | out_be32(regs + XINTC_IAR, 1 << irq); | ||
102 | } | ||
103 | |||
104 | static struct irq_chip xilinx_intc_level_irqchip = { | ||
105 | .name = "Xilinx Level INTC", | ||
106 | .irq_mask = xilinx_intc_mask, | ||
107 | .irq_mask_ack = xilinx_intc_mask, | ||
108 | .irq_unmask = xilinx_intc_level_unmask, | ||
109 | .irq_set_type = xilinx_intc_set_type, | ||
110 | }; | ||
111 | |||
112 | /* | ||
113 | * IRQ Chip edge operations | ||
114 | */ | ||
115 | static void xilinx_intc_edge_unmask(struct irq_data *d) | ||
116 | { | ||
117 | int irq = irqd_to_hwirq(d); | ||
118 | void *regs = irq_data_get_irq_chip_data(d); | ||
119 | pr_debug("unmask: %d\n", irq); | ||
120 | out_be32(regs + XINTC_SIE, 1 << irq); | ||
121 | } | ||
122 | |||
123 | static void xilinx_intc_edge_ack(struct irq_data *d) | ||
124 | { | ||
125 | int irq = irqd_to_hwirq(d); | ||
126 | void * regs = irq_data_get_irq_chip_data(d); | ||
127 | pr_debug("ack: %d\n", irq); | ||
128 | out_be32(regs + XINTC_IAR, 1 << irq); | ||
129 | } | ||
130 | |||
131 | static struct irq_chip xilinx_intc_edge_irqchip = { | ||
132 | .name = "Xilinx Edge INTC", | ||
133 | .irq_mask = xilinx_intc_mask, | ||
134 | .irq_unmask = xilinx_intc_edge_unmask, | ||
135 | .irq_ack = xilinx_intc_edge_ack, | ||
136 | .irq_set_type = xilinx_intc_set_type, | ||
137 | }; | ||
138 | |||
139 | /* | ||
140 | * IRQ Host operations | ||
141 | */ | ||
142 | |||
143 | /** | ||
144 | * xilinx_intc_xlate - translate virq# from device tree interrupts property | ||
145 | */ | ||
146 | static int xilinx_intc_xlate(struct irq_domain *h, struct device_node *ct, | ||
147 | const u32 *intspec, unsigned int intsize, | ||
148 | irq_hw_number_t *out_hwirq, | ||
149 | unsigned int *out_flags) | ||
150 | { | ||
151 | if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | /* keep a copy of the interrupt type til the interrupt is mapped | ||
155 | */ | ||
156 | xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]]; | ||
157 | |||
158 | /* Xilinx uses 2 interrupt entries, the 1st being the h/w | ||
159 | * interrupt number, the 2nd being the interrupt type, edge or level | ||
160 | */ | ||
161 | *out_hwirq = intspec[0]; | ||
162 | *out_flags = xilinx_intc_map_senses[intspec[1]]; | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | static int xilinx_intc_map(struct irq_domain *h, unsigned int virq, | ||
167 | irq_hw_number_t irq) | ||
168 | { | ||
169 | irq_set_chip_data(virq, h->host_data); | ||
170 | |||
171 | if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH || | ||
172 | xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) { | ||
173 | irq_set_chip_and_handler(virq, &xilinx_intc_level_irqchip, | ||
174 | handle_level_irq); | ||
175 | } else { | ||
176 | irq_set_chip_and_handler(virq, &xilinx_intc_edge_irqchip, | ||
177 | handle_edge_irq); | ||
178 | } | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static const struct irq_domain_ops xilinx_intc_ops = { | ||
183 | .map = xilinx_intc_map, | ||
184 | .xlate = xilinx_intc_xlate, | ||
185 | }; | ||
186 | |||
187 | struct irq_domain * __init | ||
188 | xilinx_intc_init(struct device_node *np) | ||
189 | { | ||
190 | struct irq_domain * irq; | ||
191 | void * regs; | ||
192 | |||
193 | /* Find and map the intc registers */ | ||
194 | regs = of_iomap(np, 0); | ||
195 | if (!regs) { | ||
196 | pr_err("xilinx_intc: could not map registers\n"); | ||
197 | return NULL; | ||
198 | } | ||
199 | |||
200 | /* Setup interrupt controller */ | ||
201 | out_be32(regs + XINTC_IER, 0); /* disable all irqs */ | ||
202 | out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */ | ||
203 | out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */ | ||
204 | |||
205 | /* Allocate and initialize an irq_domain structure. */ | ||
206 | irq = irq_domain_add_linear(np, XILINX_INTC_MAXIRQS, &xilinx_intc_ops, | ||
207 | regs); | ||
208 | if (!irq) | ||
209 | panic(__FILE__ ": Cannot allocate IRQ host\n"); | ||
210 | |||
211 | return irq; | ||
212 | } | ||
213 | |||
214 | int xilinx_intc_get_irq(void) | ||
215 | { | ||
216 | void * regs = master_irqhost->host_data; | ||
217 | pr_debug("get_irq:\n"); | ||
218 | return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR)); | ||
219 | } | ||
220 | 33 | ||
221 | #if defined(CONFIG_PPC_I8259) | 34 | #if defined(CONFIG_PPC_I8259) |
222 | /* | 35 | /* |
@@ -265,31 +78,11 @@ static void __init xilinx_i8259_setup_cascade(void) | |||
265 | static inline void xilinx_i8259_setup_cascade(void) { return; } | 78 | static inline void xilinx_i8259_setup_cascade(void) { return; } |
266 | #endif /* defined(CONFIG_PPC_I8259) */ | 79 | #endif /* defined(CONFIG_PPC_I8259) */ |
267 | 80 | ||
268 | static const struct of_device_id xilinx_intc_match[] __initconst = { | ||
269 | { .compatible = "xlnx,opb-intc-1.00.c", }, | ||
270 | { .compatible = "xlnx,xps-intc-1.00.a", }, | ||
271 | {} | ||
272 | }; | ||
273 | |||
274 | /* | 81 | /* |
275 | * Initialize master Xilinx interrupt controller | 82 | * Initialize master Xilinx interrupt controller |
276 | */ | 83 | */ |
277 | void __init xilinx_intc_init_tree(void) | 84 | void __init xilinx_intc_init_tree(void) |
278 | { | 85 | { |
279 | struct device_node *np; | 86 | irqchip_init(); |
280 | |||
281 | /* find top level interrupt controller */ | ||
282 | for_each_matching_node(np, xilinx_intc_match) { | ||
283 | if (!of_get_property(np, "interrupts", NULL)) | ||
284 | break; | ||
285 | } | ||
286 | BUG_ON(!np); | ||
287 | |||
288 | master_irqhost = xilinx_intc_init(np); | ||
289 | BUG_ON(!master_irqhost); | ||
290 | |||
291 | irq_set_default_host(master_irqhost); | ||
292 | of_node_put(np); | ||
293 | |||
294 | xilinx_i8259_setup_cascade(); | 87 | xilinx_i8259_setup_cascade(); |
295 | } | 88 | } |