Blame view

drivers/irqchip/irq-atmel-aic.c 7.05 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 AIC (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
  
  /* Number of irq lines managed by AIC */
  #define NR_AIC_IRQS	32
  
  #define AT91_AIC_SMR(n)			((n) * 4)
  
  #define AT91_AIC_SVR(n)			(0x80 + ((n) * 4))
  #define AT91_AIC_IVR			0x100
  #define AT91_AIC_FVR			0x104
  #define AT91_AIC_ISR			0x108
  
  #define AT91_AIC_IPR			0x10c
  #define AT91_AIC_IMR			0x110
  #define AT91_AIC_CISR			0x114
  
  #define AT91_AIC_IECR			0x120
  #define AT91_AIC_IDCR			0x124
  #define AT91_AIC_ICCR			0x128
  #define AT91_AIC_ISCR			0x12c
  #define AT91_AIC_EOICR			0x130
  #define AT91_AIC_SPU			0x134
  #define AT91_AIC_DCR			0x138
  
  static struct irq_domain *aic_domain;
  
  static asmlinkage void __exception_irq_entry
  aic_handle(struct pt_regs *regs)
  {
  	struct irq_domain_chip_generic *dgc = aic_domain->gc;
  	struct irq_chip_generic *gc = dgc->gc[0];
  	u32 irqnr;
  	u32 irqstat;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
67
68
  	irqnr = irq_reg_readl(gc, AT91_AIC_IVR);
  	irqstat = irq_reg_readl(gc, AT91_AIC_ISR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
69

b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
70
  	if (!irqstat)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
71
  		irq_reg_writel(gc, 0, AT91_AIC_EOICR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
72
  	else
841f2aa46   Marc Zyngier   irqchip: atmel-ai...
73
  		handle_domain_irq(aic_domain, irqnr, regs);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
74
75
76
77
78
79
80
81
  }
  
  static int aic_retrigger(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	/* Enable interrupt on AIC5 */
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
82
  	irq_reg_writel(gc, d->mask, AT91_AIC_ISCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
83
  	irq_gc_unlock(gc);
7177144a5   Marc Zyngier   irqchip/atmel-aic...
84
  	return 1;
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
85
86
87
88
89
90
91
  }
  
  static int aic_set_type(struct irq_data *d, unsigned type)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	unsigned int smr;
  	int ret;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
92
  	smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq));
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
93
94
95
  	ret = aic_common_set_type(d, type, &smr);
  	if (ret)
  		return ret;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
96
  	irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq));
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
97
98
99
100
101
102
103
104
105
106
  
  	return 0;
  }
  
  #ifdef CONFIG_PM
  static void aic_suspend(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
107
108
  	irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR);
  	irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
109
110
111
112
113
114
115
116
  	irq_gc_unlock(gc);
  }
  
  static void aic_resume(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
117
118
  	irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR);
  	irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
119
120
121
122
123
124
125
126
  	irq_gc_unlock(gc);
  }
  
  static void aic_pm_shutdown(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
127
128
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  	irq_gc_unlock(gc);
  }
  #else
  #define aic_suspend		NULL
  #define aic_resume		NULL
  #define aic_pm_shutdown		NULL
  #endif /* CONFIG_PM */
  
  static void __init aic_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...
147
  		irq_reg_writel(gc, 0, AT91_AIC_EOICR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
148
149
150
151
152
153
  
  	/*
  	 * 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...
154
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
155
156
  
  	/* No debugging in AIC: Debug (Protect) Control Register */
332fd7c4f   Kevin Cernekee   genirq: Generic c...
157
  	irq_reg_writel(gc, 0, AT91_AIC_DCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
158
159
  
  	/* Disable and clear all interrupts initially */
332fd7c4f   Kevin Cernekee   genirq: Generic c...
160
161
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
  	irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
162
163
  
  	for (i = 0; i < 32; i++)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
164
  		irq_reg_writel(gc, i, AT91_AIC_SVR(i));
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
165
166
167
168
169
170
171
172
173
174
  }
  
  static int aic_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)
  {
  	struct irq_domain_chip_generic *dgc = d->gc;
  	struct irq_chip_generic *gc;
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
175
  	unsigned long flags;
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	unsigned smr;
  	int idx;
  	int ret;
  
  	if (!dgc)
  		return -EINVAL;
  
  	ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
  					  out_hwirq, out_type);
  	if (ret)
  		return ret;
  
  	idx = intspec[0] / dgc->irqs_per_chip;
  	if (idx >= dgc->num_chips)
  		return -EINVAL;
  
  	gc = dgc->gc[idx];
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
193
  	irq_gc_lock_irqsave(gc, flags);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
194
  	smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq));
5fd26a0bb   Milo Kim   irqchip/atmel-aic...
195
196
  	aic_common_set_priority(intspec[2], &smr);
  	irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq));
5eb0d6eb3   Boris Brezillon   irqchip/atmel-aic...
197
  	irq_gc_unlock_irqrestore(gc, flags);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
198
199
200
201
202
203
204
205
  
  	return ret;
  }
  
  static const struct irq_domain_ops aic_irq_ops = {
  	.map	= irq_map_generic_chip,
  	.xlate	= aic_irq_domain_xlate,
  };
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
206
  static void __init at91rm9200_aic_irq_fixup(void)
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
207
  {
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
208
  	aic_common_rtc_irq_fixup();
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
209
  }
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
210
  static void __init at91sam9260_aic_irq_fixup(void)
ae25eac25   Boris BREZILLON   irqchip: atmel-ai...
211
  {
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
212
  	aic_common_rtt_irq_fixup();
ae25eac25   Boris BREZILLON   irqchip: atmel-ai...
213
  }
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
214
  static void __init at91sam9g45_aic_irq_fixup(void)
f3b7bf1bd   Boris BREZILLON   irqchip: atmel-ai...
215
  {
0a46230bf   Boris Brezillon   irqchip/atmel-aic...
216
217
  	aic_common_rtc_irq_fixup();
  	aic_common_rtt_irq_fixup();
f3b7bf1bd   Boris BREZILLON   irqchip: atmel-ai...
218
  }
c376023b7   Nicolas Pitre   irqchip: Appropri...
219
  static const struct of_device_id aic_irq_fixups[] __initconst = {
25963dbd0   Boris BREZILLON   irqchip: atmel-ai...
220
  	{ .compatible = "atmel,at91rm9200", .data = at91rm9200_aic_irq_fixup },
f3b7bf1bd   Boris BREZILLON   irqchip: atmel-ai...
221
  	{ .compatible = "atmel,at91sam9g45", .data = at91sam9g45_aic_irq_fixup },
624cba572   Boris BREZILLON   irqchip: atmel-ai...
222
  	{ .compatible = "atmel,at91sam9n12", .data = at91rm9200_aic_irq_fixup },
f3b7bf1bd   Boris BREZILLON   irqchip: atmel-ai...
223
  	{ .compatible = "atmel,at91sam9rl", .data = at91sam9g45_aic_irq_fixup },
624cba572   Boris BREZILLON   irqchip: atmel-ai...
224
  	{ .compatible = "atmel,at91sam9x5", .data = at91rm9200_aic_irq_fixup },
ae25eac25   Boris BREZILLON   irqchip: atmel-ai...
225
226
227
228
  	{ .compatible = "atmel,at91sam9260", .data = at91sam9260_aic_irq_fixup },
  	{ .compatible = "atmel,at91sam9261", .data = at91sam9260_aic_irq_fixup },
  	{ .compatible = "atmel,at91sam9263", .data = at91sam9260_aic_irq_fixup },
  	{ .compatible = "atmel,at91sam9g20", .data = at91sam9260_aic_irq_fixup },
6704d12d6   Boris BREZILLON   irqchip: atmel-ai...
229
230
  	{ /* sentinel */ },
  };
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
231
232
233
234
235
236
237
238
239
240
  static int __init aic_of_init(struct device_node *node,
  			      struct device_node *parent)
  {
  	struct irq_chip_generic *gc;
  	struct irq_domain *domain;
  
  	if (aic_domain)
  		return -EEXIST;
  
  	domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic",
dd85c7915   Milo Kim   irqchip/atmel-aic...
241
  				    NR_AIC_IRQS, aic_irq_fixups);
b1479ebb7   Boris BREZILLON   irqchip: atmel-ai...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  	if (IS_ERR(domain))
  		return PTR_ERR(domain);
  
  	aic_domain = domain;
  	gc = irq_get_domain_generic_chip(domain, 0);
  
  	gc->chip_types[0].regs.eoi = AT91_AIC_EOICR;
  	gc->chip_types[0].regs.enable = AT91_AIC_IECR;
  	gc->chip_types[0].regs.disable = AT91_AIC_IDCR;
  	gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
  	gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
  	gc->chip_types[0].chip.irq_retrigger = aic_retrigger;
  	gc->chip_types[0].chip.irq_set_type = aic_set_type;
  	gc->chip_types[0].chip.irq_suspend = aic_suspend;
  	gc->chip_types[0].chip.irq_resume = aic_resume;
  	gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown;
  
  	aic_hw_init(domain);
  	set_handle_irq(aic_handle);
  
  	return 0;
  }
  IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init);