Blame view
drivers/irqchip/irq-atmel-aic.c
7.05 KB
b1479ebb7 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 irqchip: Prepare ... |
22 |
#include <linux/irqchip.h> |
b1479ebb7 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 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 genirq: Generic c... |
67 68 |
irqnr = irq_reg_readl(gc, AT91_AIC_IVR); irqstat = irq_reg_readl(gc, AT91_AIC_ISR); |
b1479ebb7 irqchip: atmel-ai... |
69 |
|
b1479ebb7 irqchip: atmel-ai... |
70 |
if (!irqstat) |
332fd7c4f genirq: Generic c... |
71 |
irq_reg_writel(gc, 0, AT91_AIC_EOICR); |
b1479ebb7 irqchip: atmel-ai... |
72 |
else |
841f2aa46 irqchip: atmel-ai... |
73 |
handle_domain_irq(aic_domain, irqnr, regs); |
b1479ebb7 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 genirq: Generic c... |
82 |
irq_reg_writel(gc, d->mask, AT91_AIC_ISCR); |
b1479ebb7 irqchip: atmel-ai... |
83 84 85 86 87 88 89 90 91 92 |
irq_gc_unlock(gc); return 0; } 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 genirq: Generic c... |
93 |
smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq)); |
b1479ebb7 irqchip: atmel-ai... |
94 95 96 |
ret = aic_common_set_type(d, type, &smr); if (ret) return ret; |
332fd7c4f genirq: Generic c... |
97 |
irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq)); |
b1479ebb7 irqchip: atmel-ai... |
98 99 100 101 102 103 104 105 106 107 |
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 genirq: Generic c... |
108 109 |
irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR); irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR); |
b1479ebb7 irqchip: atmel-ai... |
110 111 112 113 114 115 116 117 |
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 genirq: Generic c... |
118 119 |
irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR); irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR); |
b1479ebb7 irqchip: atmel-ai... |
120 121 122 123 124 125 126 127 |
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 genirq: Generic c... |
128 129 |
irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); |
b1479ebb7 irqchip: atmel-ai... |
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
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 genirq: Generic c... |
148 |
irq_reg_writel(gc, 0, AT91_AIC_EOICR); |
b1479ebb7 irqchip: atmel-ai... |
149 150 151 152 153 154 |
/* * 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 genirq: Generic c... |
155 |
irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU); |
b1479ebb7 irqchip: atmel-ai... |
156 157 |
/* No debugging in AIC: Debug (Protect) Control Register */ |
332fd7c4f genirq: Generic c... |
158 |
irq_reg_writel(gc, 0, AT91_AIC_DCR); |
b1479ebb7 irqchip: atmel-ai... |
159 160 |
/* Disable and clear all interrupts initially */ |
332fd7c4f genirq: Generic c... |
161 162 |
irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); |
b1479ebb7 irqchip: atmel-ai... |
163 164 |
for (i = 0; i < 32; i++) |
332fd7c4f genirq: Generic c... |
165 |
irq_reg_writel(gc, i, AT91_AIC_SVR(i)); |
b1479ebb7 irqchip: atmel-ai... |
166 167 168 169 170 171 172 173 174 175 |
} 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 irqchip/atmel-aic... |
176 |
unsigned long flags; |
b1479ebb7 irqchip: atmel-ai... |
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
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 irqchip/atmel-aic... |
194 |
irq_gc_lock_irqsave(gc, flags); |
332fd7c4f genirq: Generic c... |
195 |
smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); |
5fd26a0bb irqchip/atmel-aic... |
196 197 |
aic_common_set_priority(intspec[2], &smr); irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); |
5eb0d6eb3 irqchip/atmel-aic... |
198 |
irq_gc_unlock_irqrestore(gc, flags); |
b1479ebb7 irqchip: atmel-ai... |
199 200 201 202 203 204 205 206 |
return ret; } static const struct irq_domain_ops aic_irq_ops = { .map = irq_map_generic_chip, .xlate = aic_irq_domain_xlate, }; |
0a46230bf irqchip/atmel-aic... |
207 |
static void __init at91rm9200_aic_irq_fixup(void) |
6704d12d6 irqchip: atmel-ai... |
208 |
{ |
0a46230bf irqchip/atmel-aic... |
209 |
aic_common_rtc_irq_fixup(); |
6704d12d6 irqchip: atmel-ai... |
210 |
} |
0a46230bf irqchip/atmel-aic... |
211 |
static void __init at91sam9260_aic_irq_fixup(void) |
ae25eac25 irqchip: atmel-ai... |
212 |
{ |
0a46230bf irqchip/atmel-aic... |
213 |
aic_common_rtt_irq_fixup(); |
ae25eac25 irqchip: atmel-ai... |
214 |
} |
0a46230bf irqchip/atmel-aic... |
215 |
static void __init at91sam9g45_aic_irq_fixup(void) |
f3b7bf1bd irqchip: atmel-ai... |
216 |
{ |
0a46230bf irqchip/atmel-aic... |
217 218 |
aic_common_rtc_irq_fixup(); aic_common_rtt_irq_fixup(); |
f3b7bf1bd irqchip: atmel-ai... |
219 |
} |
c376023b7 irqchip: Appropri... |
220 |
static const struct of_device_id aic_irq_fixups[] __initconst = { |
25963dbd0 irqchip: atmel-ai... |
221 |
{ .compatible = "atmel,at91rm9200", .data = at91rm9200_aic_irq_fixup }, |
f3b7bf1bd irqchip: atmel-ai... |
222 |
{ .compatible = "atmel,at91sam9g45", .data = at91sam9g45_aic_irq_fixup }, |
624cba572 irqchip: atmel-ai... |
223 |
{ .compatible = "atmel,at91sam9n12", .data = at91rm9200_aic_irq_fixup }, |
f3b7bf1bd irqchip: atmel-ai... |
224 |
{ .compatible = "atmel,at91sam9rl", .data = at91sam9g45_aic_irq_fixup }, |
624cba572 irqchip: atmel-ai... |
225 |
{ .compatible = "atmel,at91sam9x5", .data = at91rm9200_aic_irq_fixup }, |
ae25eac25 irqchip: atmel-ai... |
226 227 228 229 |
{ .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 irqchip: atmel-ai... |
230 231 |
{ /* sentinel */ }, }; |
b1479ebb7 irqchip: atmel-ai... |
232 233 234 235 236 237 238 239 240 241 |
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 irqchip/atmel-aic... |
242 |
NR_AIC_IRQS, aic_irq_fixups); |
b1479ebb7 irqchip: atmel-ai... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
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); |