File Index
Files and directories
Sign in
FILE

irq-atmel-aic5.c

File preview with syntax highlighting.

linux-6.19.14/drivers/irqchip/irq-atmel-aic5.c C
  1/*
  2 * Atmel AT91 AIC5 (Advanced Interrupt Controller) driver
  3 *
  4 *  Copyright (C) 2004 SAN People
  5 *  Copyright (C) 2004 ATMEL
  6 *  Copyright (C) Rick Bronson
  7 *  Copyright (C) 2014 Free Electrons
  8 *
  9 *  Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
 10 *
 11 * This file is licensed under the terms of the GNU General Public
 12 * License version 2.  This program is licensed "as is" without any
 13 * warranty of any kind, whether express or implied.
 14 */
 15
 16#include <linux/init.h>
 17#include <linux/module.h>
 18#include <linux/mm.h>
 19#include <linux/bitmap.h>
 20#include <linux/types.h>
 21#include <linux/irq.h>
 22#include <linux/irqchip.h>
 23#include <linux/of.h>
 24#include <linux/of_address.h>
 25#include <linux/of_irq.h>
 26#include <linux/irqdomain.h>
 27#include <linux/err.h>
 28#include <linux/slab.h>
 29#include <linux/io.h>
 30
 31#include <asm/exception.h>
 32#include <asm/mach/irq.h>
 33
 34#include "irq-atmel-aic-common.h"
 35
 36/* Number of irq lines managed by AIC */
 37#define NR_AIC5_IRQS	128
 38
 39#define AT91_AIC5_SSR		0x0
 40#define AT91_AIC5_INTSEL_MSK	(0x7f << 0)
 41
 42#define AT91_AIC5_SMR			0x4
 43
 44#define AT91_AIC5_SVR			0x8
 45#define AT91_AIC5_IVR			0x10
 46#define AT91_AIC5_FVR			0x14
 47#define AT91_AIC5_ISR			0x18
 48
 49#define AT91_AIC5_IPR0			0x20
 50#define AT91_AIC5_IPR1			0x24
 51#define AT91_AIC5_IPR2			0x28
 52#define AT91_AIC5_IPR3			0x2c
 53#define AT91_AIC5_IMR			0x30
 54#define AT91_AIC5_CISR			0x34
 55
 56#define AT91_AIC5_IECR			0x40
 57#define AT91_AIC5_IDCR			0x44
 58#define AT91_AIC5_ICCR			0x48
 59#define AT91_AIC5_ISCR			0x4c
 60#define AT91_AIC5_EOICR			0x38
 61#define AT91_AIC5_SPU			0x3c
 62#define AT91_AIC5_DCR			0x6c
 63
 64#define AT91_AIC5_FFER			0x50
 65#define AT91_AIC5_FFDR			0x54
 66#define AT91_AIC5_FFSR			0x58
 67
 68static struct irq_domain *aic5_domain;
 69
 70static void __exception_irq_entry aic5_handle(struct pt_regs *regs)
 71{
 72	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
 73	u32 irqnr;
 74	u32 irqstat;
 75
 76	irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
 77	irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
 78
 79	if (!irqstat)
 80		irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
 81	else
 82		generic_handle_domain_irq(aic5_domain, irqnr);
 83}
 84
 85static void aic5_mask(struct irq_data *d)
 86{
 87	struct irq_domain *domain = d->domain;
 88	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 89	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 90
 91	/*
 92	 * Disable interrupt on AIC5. We always take the lock of the
 93	 * first irq chip as all chips share the same registers.
 94	 */
 95	guard(raw_spinlock)(&bgc->lock);
 96	irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
 97	irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
 98	gc->mask_cache &= ~d->mask;
 99}
100
101static void aic5_unmask(struct irq_data *d)
102{
103	struct irq_domain *domain = d->domain;
104	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
105	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
106
107	/*
108	 * Enable interrupt on AIC5. We always take the lock of the
109	 * first irq chip as all chips share the same registers.
110	 */
111	guard(raw_spinlock)(&bgc->lock);
112	irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
113	irq_reg_writel(gc, 1, AT91_AIC5_IECR);
114	gc->mask_cache |= d->mask;
115}
116
117static int aic5_retrigger(struct irq_data *d)
118{
119	struct irq_domain *domain = d->domain;
120	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
121
122	/* Enable interrupt on AIC5 */
123	guard(raw_spinlock)(&bgc->lock);
124	irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
125	irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
126	return 1;
127}
128
129static int aic5_set_type(struct irq_data *d, unsigned type)
130{
131	struct irq_domain *domain = d->domain;
132	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
133	unsigned int smr;
134	int ret;
135
136	guard(raw_spinlock)(&bgc->lock);
137	irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
138	smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
139	ret = aic_common_set_type(d, type, &smr);
140	if (!ret)
141		irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
142	return ret;
143}
144
145#ifdef CONFIG_PM
146static u32 *smr_cache;
147
148static void aic5_suspend(struct irq_data *d)
149{
150	struct irq_domain *domain = d->domain;
151	struct irq_domain_chip_generic *dgc = domain->gc;
152	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
153	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
154	int i;
155	u32 mask;
156
157	if (smr_cache)
158		for (i = 0; i < domain->revmap_size; i++) {
159			irq_reg_writel(bgc, i, AT91_AIC5_SSR);
160			smr_cache[i] = irq_reg_readl(bgc, AT91_AIC5_SMR);
161		}
162
163	guard(raw_spinlock)(&bgc->lock);
164	for (i = 0; i < dgc->irqs_per_chip; i++) {
165		mask = 1 << i;
166		if ((mask & gc->mask_cache) == (mask & gc->wake_active))
167			continue;
168
169		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
170		if (mask & gc->wake_active)
171			irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
172		else
173			irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
174	}
175}
176
177static void aic5_resume(struct irq_data *d)
178{
179	struct irq_domain *domain = d->domain;
180	struct irq_domain_chip_generic *dgc = domain->gc;
181	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
182	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
183	int i;
184	u32 mask;
185
186	guard(raw_spinlock)(&bgc->lock);
187
188	if (smr_cache) {
189		irq_reg_writel(bgc, 0xffffffff, AT91_AIC5_SPU);
190		for (i = 0; i < domain->revmap_size; i++) {
191			irq_reg_writel(bgc, i, AT91_AIC5_SSR);
192			irq_reg_writel(bgc, i, AT91_AIC5_SVR);
193			irq_reg_writel(bgc, smr_cache[i], AT91_AIC5_SMR);
194		}
195	}
196
197	for (i = 0; i < dgc->irqs_per_chip; i++) {
198		mask = 1 << i;
199
200		if (!smr_cache &&
201		    ((mask & gc->mask_cache) == (mask & gc->wake_active)))
202			continue;
203
204		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
205		if (mask & gc->mask_cache)
206			irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
207		else
208			irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
209	}
210}
211
212static void aic5_pm_shutdown(struct irq_data *d)
213{
214	struct irq_domain *domain = d->domain;
215	struct irq_domain_chip_generic *dgc = domain->gc;
216	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
217	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
218	int i;
219
220	guard(raw_spinlock)(&bgc->lock);
221	for (i = 0; i < dgc->irqs_per_chip; i++) {
222		irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
223		irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
224		irq_reg_writel(bgc, 1, AT91_AIC5_ICCR);
225	}
226}
227#else
228#define aic5_suspend		NULL
229#define aic5_resume		NULL
230#define aic5_pm_shutdown	NULL
231#endif /* CONFIG_PM */
232
233static void __init aic5_hw_init(struct irq_domain *domain)
234{
235	struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
236	int i;
237
238	/*
239	 * Perform 8 End Of Interrupt Command to make sure AIC
240	 * will not Lock out nIRQ
241	 */
242	for (i = 0; i < 8; i++)
243		irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
244
245	/*
246	 * Spurious Interrupt ID in Spurious Vector Register.
247	 * When there is no current interrupt, the IRQ Vector Register
248	 * reads the value stored in AIC_SPU
249	 */
250	irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU);
251
252	/* No debugging in AIC: Debug (Protect) Control Register */
253	irq_reg_writel(gc, 0, AT91_AIC5_DCR);
254
255	/* Disable and clear all interrupts initially */
256	for (i = 0; i < domain->revmap_size; i++) {
257		irq_reg_writel(gc, i, AT91_AIC5_SSR);
258		irq_reg_writel(gc, i, AT91_AIC5_SVR);
259		irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
260		irq_reg_writel(gc, 1, AT91_AIC5_ICCR);
261	}
262}
263
264static int aic5_irq_domain_xlate(struct irq_domain *d,
265				 struct device_node *ctrlr,
266				 const u32 *intspec, unsigned int intsize,
267				 irq_hw_number_t *out_hwirq,
268				 unsigned int *out_type)
269{
270	struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
271	unsigned smr;
272	int ret;
273
274	if (!bgc)
275		return -EINVAL;
276
277	ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
278					  out_hwirq, out_type);
279	if (ret)
280		return ret;
281
282	guard(raw_spinlock_irqsave)(&bgc->lock);
283	irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
284	smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
285	aic_common_set_priority(intspec[2], &smr);
286	irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
287	return ret;
288}
289
290static const struct irq_domain_ops aic5_irq_ops = {
291	.map	= irq_map_generic_chip,
292	.xlate	= aic5_irq_domain_xlate,
293};
294
295static void __init sama5d3_aic_irq_fixup(void)
296{
297	aic_common_rtc_irq_fixup();
298}
299
300static void __init sam9x60_aic_irq_fixup(void)
301{
302	aic_common_rtc_irq_fixup();
303	aic_common_rtt_irq_fixup();
304}
305
306static const struct of_device_id aic5_irq_fixups[] __initconst = {
307	{ .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
308	{ .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
309	{ .compatible = "microchip,sam9x60", .data = sam9x60_aic_irq_fixup },
310	{ .compatible = "microchip,sam9x7", .data = sam9x60_aic_irq_fixup },
311	{ /* sentinel */ },
312};
313
314static int __init aic5_of_init(struct device_node *node,
315			       struct device_node *parent,
316			       int nirqs)
317{
318	struct irq_chip_generic *gc;
319	struct irq_domain *domain;
320	int nchips;
321	int i;
322
323	if (nirqs > NR_AIC5_IRQS)
324		return -EINVAL;
325
326	if (aic5_domain)
327		return -EEXIST;
328
329	domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
330				    nirqs, aic5_irq_fixups);
331	if (IS_ERR(domain))
332		return PTR_ERR(domain);
333
334	aic5_domain = domain;
335	nchips = aic5_domain->revmap_size / 32;
336	for (i = 0; i < nchips; i++) {
337		gc = irq_get_domain_generic_chip(domain, i * 32);
338
339		gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
340		gc->chip_types[0].chip.irq_mask = aic5_mask;
341		gc->chip_types[0].chip.irq_unmask = aic5_unmask;
342		gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
343		gc->chip_types[0].chip.irq_set_type = aic5_set_type;
344		gc->chip_types[0].chip.irq_suspend = aic5_suspend;
345		gc->chip_types[0].chip.irq_resume = aic5_resume;
346		gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
347	}
348
349	aic5_hw_init(domain);
350	set_handle_irq(aic5_handle);
351
352	return 0;
353}
354
355#define NR_SAMA5D2_IRQS		77
356
357static int __init sama5d2_aic5_of_init(struct device_node *node,
358				       struct device_node *parent)
359{
360#ifdef CONFIG_PM
361	smr_cache = kcalloc(DIV_ROUND_UP(NR_SAMA5D2_IRQS, 32) * 32,
362			    sizeof(*smr_cache), GFP_KERNEL);
363	if (!smr_cache)
364		return -ENOMEM;
365#endif
366
367	return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
368}
369IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
370
371#define NR_SAMA5D3_IRQS		48
372
373static int __init sama5d3_aic5_of_init(struct device_node *node,
374				       struct device_node *parent)
375{
376	return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
377}
378IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
379
380#define NR_SAMA5D4_IRQS		68
381
382static int __init sama5d4_aic5_of_init(struct device_node *node,
383				       struct device_node *parent)
384{
385	return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
386}
387IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init);
388
389#define NR_SAM9X60_IRQS		50
390
391static int __init sam9x60_aic5_of_init(struct device_node *node,
392				       struct device_node *parent)
393{
394	return aic5_of_init(node, parent, NR_SAM9X60_IRQS);
395}
396IRQCHIP_DECLARE(sam9x60_aic5, "microchip,sam9x60-aic", sam9x60_aic5_of_init);
397
398#define NR_SAM9X7_IRQS		70
399
400static int __init sam9x7_aic5_of_init(struct device_node *node, struct device_node *parent)
401{
402	return aic5_of_init(node, parent, NR_SAM9X7_IRQS);
403}
404IRQCHIP_DECLARE(sam9x7_aic5, "microchip,sam9x7-aic", sam9x7_aic5_of_init);
~/ Read-only file preview.