Blame view

drivers/irqchip/irq-atmel-aic5.c 9.52 KB
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * Atmel AT91 AIC5 (Advanced Interrupt Controller) driver
   *
   *  Copyright (C) 2004 SAN People
   *  Copyright (C) 2004 ATMEL
   *  Copyright (C) Rick Bronson
   *  Copyright (C) 2014 Free Electrons
   *
   *  Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2.  This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/mm.h>
  #include <linux/bitmap.h>
  #include <linux/types.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
22
  #include <linux/irqchip.h>
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
23
24
25
26
27
28
29
30
31
32
33
34
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  #include <linux/irqdomain.h>
  #include <linux/err.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  
  #include <asm/exception.h>
  #include <asm/mach/irq.h>
  
  #include "irq-atmel-aic-common.h"
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  
  /* Number of irq lines managed by AIC */
  #define NR_AIC5_IRQS	128
  
  #define AT91_AIC5_SSR		0x0
  #define AT91_AIC5_INTSEL_MSK	(0x7f << 0)
  
  #define AT91_AIC5_SMR			0x4
  
  #define AT91_AIC5_SVR			0x8
  #define AT91_AIC5_IVR			0x10
  #define AT91_AIC5_FVR			0x14
  #define AT91_AIC5_ISR			0x18
  
  #define AT91_AIC5_IPR0			0x20
  #define AT91_AIC5_IPR1			0x24
  #define AT91_AIC5_IPR2			0x28
  #define AT91_AIC5_IPR3			0x2c
  #define AT91_AIC5_IMR			0x30
  #define AT91_AIC5_CISR			0x34
  
  #define AT91_AIC5_IECR			0x40
  #define AT91_AIC5_IDCR			0x44
  #define AT91_AIC5_ICCR			0x48
  #define AT91_AIC5_ISCR			0x4c
  #define AT91_AIC5_EOICR			0x38
  #define AT91_AIC5_SPU			0x3c
  #define AT91_AIC5_DCR			0x6c
  
  #define AT91_AIC5_FFER			0x50
  #define AT91_AIC5_FFDR			0x54
  #define AT91_AIC5_FFSR			0x58
  
  static struct irq_domain *aic5_domain;
  
  static asmlinkage void __exception_irq_entry
  aic5_handle(struct pt_regs *regs)
  {
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
73
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
74
75
  	u32 irqnr;
  	u32 irqstat;
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
76
77
  	irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
  	irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
78

b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
79
  	if (!irqstat)
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
80
  		irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
81
  	else
31b7b6a86   Marc Zyngier   irqchip: atmel-ai...
82
  		handle_domain_irq(aic5_domain, irqnr, regs);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
83
84
85
86
87
  }
  
  static void aic5_mask(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
88
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
d32dc9aa1   Ludovic Desroches   irqchip/atmel-aic...
89
90
91
92
93
94
95
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	/*
  	 * Disable interrupt on AIC5. We always take the lock of the
  	 * first irq chip as all chips share the same registers.
  	 */
  	irq_gc_lock(bgc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
96
97
  	irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
  	irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
98
  	gc->mask_cache &= ~d->mask;
d32dc9aa1   Ludovic Desroches   irqchip/atmel-aic...
99
  	irq_gc_unlock(bgc);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
100
101
102
103
104
  }
  
  static void aic5_unmask(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
105
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
d32dc9aa1   Ludovic Desroches   irqchip/atmel-aic...
106
107
108
109
110
111
112
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	/*
  	 * Enable interrupt on AIC5. We always take the lock of the
  	 * first irq chip as all chips share the same registers.
  	 */
  	irq_gc_lock(bgc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
113
114
  	irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
  	irq_reg_writel(gc, 1, AT91_AIC5_IECR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
115
  	gc->mask_cache |= d->mask;
d32dc9aa1   Ludovic Desroches   irqchip/atmel-aic...
116
  	irq_gc_unlock(bgc);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
117
118
119
120
121
  }
  
  static int aic5_retrigger(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
122
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
123
124
  
  	/* Enable interrupt on AIC5 */
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
125
126
127
128
  	irq_gc_lock(bgc);
  	irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
  	irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
  	irq_gc_unlock(bgc);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
129
130
131
132
133
134
135
  
  	return 0;
  }
  
  static int aic5_set_type(struct irq_data *d, unsigned type)
  {
  	struct irq_domain *domain = d->domain;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
136
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
137
138
  	unsigned int smr;
  	int ret;
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
139
140
141
  	irq_gc_lock(bgc);
  	irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
  	smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
142
143
  	ret = aic_common_set_type(d, type, &smr);
  	if (!ret)
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
144
145
  		irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
  	irq_gc_unlock(bgc);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
146
147
148
149
150
151
152
153
154
  
  	return ret;
  }
  
  #ifdef CONFIG_PM
  static void aic5_suspend(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
  	struct irq_domain_chip_generic *dgc = domain->gc;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
155
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
156
157
158
159
160
161
162
163
164
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	int i;
  	u32 mask;
  
  	irq_gc_lock(bgc);
  	for (i = 0; i < dgc->irqs_per_chip; i++) {
  		mask = 1 << i;
  		if ((mask & gc->mask_cache) == (mask & gc->wake_active))
  			continue;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
165
  		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
166
  		if (mask & gc->wake_active)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
167
  			irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
168
  		else
332fd7c4f   Kevin Cernekee   genirq: Generic c...
169
  			irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
170
171
172
173
174
175
176
177
  	}
  	irq_gc_unlock(bgc);
  }
  
  static void aic5_resume(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
  	struct irq_domain_chip_generic *dgc = domain->gc;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
178
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
179
180
181
182
183
184
185
186
187
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	int i;
  	u32 mask;
  
  	irq_gc_lock(bgc);
  	for (i = 0; i < dgc->irqs_per_chip; i++) {
  		mask = 1 << i;
  		if ((mask & gc->mask_cache) == (mask & gc->wake_active))
  			continue;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
188
  		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
189
  		if (mask & gc->mask_cache)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
190
  			irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
191
  		else
332fd7c4f   Kevin Cernekee   genirq: Generic c...
192
  			irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
193
194
195
196
197
198
199
200
  	}
  	irq_gc_unlock(bgc);
  }
  
  static void aic5_pm_shutdown(struct irq_data *d)
  {
  	struct irq_domain *domain = d->domain;
  	struct irq_domain_chip_generic *dgc = domain->gc;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
201
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
202
203
204
205
206
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	int i;
  
  	irq_gc_lock(bgc);
  	for (i = 0; i < dgc->irqs_per_chip; i++) {
332fd7c4f   Kevin Cernekee   genirq: Generic c...
207
208
209
  		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
  		irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
  		irq_reg_writel(bgc, 1, AT91_AIC5_ICCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  	}
  	irq_gc_unlock(bgc);
  }
  #else
  #define aic5_suspend		NULL
  #define aic5_resume		NULL
  #define aic5_pm_shutdown	NULL
  #endif /* CONFIG_PM */
  
  static void __init aic5_hw_init(struct irq_domain *domain)
  {
  	struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
  	int i;
  
  	/*
  	 * Perform 8 End Of Interrupt Command to make sure AIC
  	 * will not Lock out nIRQ
  	 */
  	for (i = 0; i < 8; i++)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
229
  		irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
230
231
232
233
234
235
  
  	/*
  	 * Spurious Interrupt ID in Spurious Vector Register.
  	 * When there is no current interrupt, the IRQ Vector Register
  	 * reads the value stored in AIC_SPU
  	 */
332fd7c4f   Kevin Cernekee   genirq: Generic c...
236
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
237
238
  
  	/* No debugging in AIC: Debug (Protect) Control Register */
332fd7c4f   Kevin Cernekee   genirq: Generic c...
239
  	irq_reg_writel(gc, 0, AT91_AIC5_DCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
240
241
242
  
  	/* Disable and clear all interrupts initially */
  	for (i = 0; i < domain->revmap_size; i++) {
332fd7c4f   Kevin Cernekee   genirq: Generic c...
243
244
245
246
  		irq_reg_writel(gc, i, AT91_AIC5_SSR);
  		irq_reg_writel(gc, i, AT91_AIC5_SVR);
  		irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
  		irq_reg_writel(gc, 1, AT91_AIC5_ICCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
247
248
249
250
251
252
253
254
255
  	}
  }
  
  static int aic5_irq_domain_xlate(struct irq_domain *d,
  				 struct device_node *ctrlr,
  				 const u32 *intspec, unsigned int intsize,
  				 irq_hw_number_t *out_hwirq,
  				 unsigned int *out_type)
  {
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
256
  	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
257
  	unsigned long flags;
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
258
259
  	unsigned smr;
  	int ret;
b55a3bb86   Ludovic Desroches   irqchip/atmel-aic...
260
  	if (!bgc)
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
261
262
263
264
265
266
  		return -EINVAL;
  
  	ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
  					  out_hwirq, out_type);
  	if (ret)
  		return ret;
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
267
  	irq_gc_lock_irqsave(bgc, flags);
414a431ad   Ludovic Desroches   irqchip/atmel-aic...
268
269
  	irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
  	smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
5fd26a0bb   Milo Kim   irqchip/atmel-aic...
270
  	aic_common_set_priority(intspec[2], &smr);
4b5ce20b5   Milo Kim   irqchip/atmel-aic...
271
  	irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
272
  	irq_gc_unlock_irqrestore(bgc, flags);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
273
274
275
276
277
278
279
280
  
  	return ret;
  }
  
  static const struct irq_domain_ops aic5_irq_ops = {
  	.map	= irq_map_generic_chip,
  	.xlate	= aic5_irq_domain_xlate,
  };
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
281
282
283
284
  static void __init sama5d3_aic_irq_fixup(struct device_node *root)
  {
  	aic_common_rtc_irq_fixup(root);
  }
c376023b7   Nicolas Pitre   irqchip: Appropri...
285
  static const struct of_device_id aic5_irq_fixups[] __initconst = {
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
286
  	{ .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
20afdeb81   Alexandre Belloni   irqchip: atmel-ai...
287
  	{ .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
288
289
  	{ /* sentinel */ },
  };
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  static int __init aic5_of_init(struct device_node *node,
  			       struct device_node *parent,
  			       int nirqs)
  {
  	struct irq_chip_generic *gc;
  	struct irq_domain *domain;
  	int nchips;
  	int i;
  
  	if (nirqs > NR_AIC5_IRQS)
  		return -EINVAL;
  
  	if (aic5_domain)
  		return -EEXIST;
  
  	domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
dd85c7915   Milo Kim   irqchip/atmel-aic...
306
  				    nirqs, aic5_irq_fixups);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  	if (IS_ERR(domain))
  		return PTR_ERR(domain);
  
  	aic5_domain = domain;
  	nchips = aic5_domain->revmap_size / 32;
  	for (i = 0; i < nchips; i++) {
  		gc = irq_get_domain_generic_chip(domain, i * 32);
  
  		gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
  		gc->chip_types[0].chip.irq_mask = aic5_mask;
  		gc->chip_types[0].chip.irq_unmask = aic5_unmask;
  		gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
  		gc->chip_types[0].chip.irq_set_type = aic5_set_type;
  		gc->chip_types[0].chip.irq_suspend = aic5_suspend;
  		gc->chip_types[0].chip.irq_resume = aic5_resume;
  		gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
  	}
  
  	aic5_hw_init(domain);
  	set_handle_irq(aic5_handle);
  
  	return 0;
  }
62a993df3   Nicolas Ferre   irqchip: atmel-ai...
330
331
332
333
334
335
336
337
  #define NR_SAMA5D2_IRQS		77
  
  static int __init sama5d2_aic5_of_init(struct device_node *node,
  				       struct device_node *parent)
  {
  	return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
  }
  IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
0cae165ff   Alexandre Belloni   irqchip: atmel-ai...
338
  #define NR_SAMA5D3_IRQS		48
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
339
340
341
342
343
344
345
  
  static int __init sama5d3_aic5_of_init(struct device_node *node,
  				       struct device_node *parent)
  {
  	return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
  }
  IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
20afdeb81   Alexandre Belloni   irqchip: atmel-ai...
346
347
348
349
350
351
352
353
354
  
  #define NR_SAMA5D4_IRQS		68
  
  static int __init sama5d4_aic5_of_init(struct device_node *node,
  				       struct device_node *parent)
  {
  	return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
  }
  IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init);