diff options
| author | Jason Cooper <jason@lakedaemon.net> | 2014-07-18 16:58:34 -0400 |
|---|---|---|
| committer | Jason Cooper <jason@lakedaemon.net> | 2014-07-18 16:58:34 -0400 |
| commit | f0cf9d2facbe3aa93b302058c013729cbc1bca22 (patch) | |
| tree | 272bdf38e50d723fc88ef1fa068582087b6b8b27 | |
| parent | 20c0c607605244dd237707bb07e7f2ffd82e8aa5 (diff) | |
| parent | 6704d12d688192366f3a70e6f8a85cb5a869cd5a (diff) | |
Merge branch 'irqchip/atmel-aic' into irqchip/core
Topic branch set up to facilitate merging the rest of the series which
removes the driver from arch code.
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt (renamed from Documentation/devicetree/bindings/arm/atmel-aic.txt) | 0 | ||||
| -rw-r--r-- | drivers/irqchip/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-atmel-aic-common.c | 254 | ||||
| -rw-r--r-- | drivers/irqchip/irq-atmel-aic-common.h | 39 | ||||
| -rw-r--r-- | drivers/irqchip/irq-atmel-aic.c | 262 | ||||
| -rw-r--r-- | drivers/irqchip/irq-atmel-aic5.c | 353 | ||||
| -rw-r--r-- | include/linux/irq.h | 2 | ||||
| -rw-r--r-- | kernel/irq/generic-chip.c | 5 |
9 files changed, 929 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/arm/atmel-aic.txt b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt index 2742e9cfd6b1..2742e9cfd6b1 100644 --- a/Documentation/devicetree/bindings/arm/atmel-aic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt | |||
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 5b256ed37d96..4e230e7c76ee 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
| @@ -35,6 +35,20 @@ config ARM_VIC_NR | |||
| 35 | The maximum number of VICs available in the system, for | 35 | The maximum number of VICs available in the system, for |
| 36 | power management. | 36 | power management. |
| 37 | 37 | ||
| 38 | config ATMEL_AIC_IRQ | ||
| 39 | bool | ||
| 40 | select GENERIC_IRQ_CHIP | ||
| 41 | select IRQ_DOMAIN | ||
| 42 | select MULTI_IRQ_HANDLER | ||
| 43 | select SPARSE_IRQ | ||
| 44 | |||
| 45 | config ATMEL_AIC5_IRQ | ||
| 46 | bool | ||
| 47 | select GENERIC_IRQ_CHIP | ||
| 48 | select IRQ_DOMAIN | ||
| 49 | select MULTI_IRQ_HANDLER | ||
| 50 | select SPARSE_IRQ | ||
| 51 | |||
| 38 | config BRCMSTB_L2_IRQ | 52 | config BRCMSTB_L2_IRQ |
| 39 | bool | 53 | bool |
| 40 | depends on ARM | 54 | depends on ARM |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 7638221ee552..73052ba9ca62 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
| @@ -20,6 +20,8 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o | |||
| 20 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o | 20 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o |
| 21 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o | 21 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o |
| 22 | obj-$(CONFIG_ARM_VIC) += irq-vic.o | 22 | obj-$(CONFIG_ARM_VIC) += irq-vic.o |
| 23 | obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o | ||
| 24 | obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o | ||
| 23 | obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o | 25 | obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o |
| 24 | obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o | 26 | obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o |
| 25 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o | 27 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o |
diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c new file mode 100644 index 000000000000..6ae3cdee0681 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic-common.c | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | /* | ||
| 2 | * Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by | ||
| 3 | * irq-atmel-aic and irq-atmel-aic5 drivers | ||
| 4 | * | ||
| 5 | * Copyright (C) 2004 SAN People | ||
| 6 | * Copyright (C) 2004 ATMEL | ||
| 7 | * Copyright (C) Rick Bronson | ||
| 8 | * Copyright (C) 2014 Free Electrons | ||
| 9 | * | ||
| 10 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | ||
| 11 | * | ||
| 12 | * This file is licensed under the terms of the GNU General Public | ||
| 13 | * License version 2. This program is licensed "as is" without any | ||
| 14 | * warranty of any kind, whether express or implied. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/errno.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/irq.h> | ||
| 20 | #include <linux/irqdomain.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/of_address.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | |||
| 25 | #include "irq-atmel-aic-common.h" | ||
| 26 | |||
| 27 | #define AT91_AIC_PRIOR GENMASK(2, 0) | ||
| 28 | #define AT91_AIC_IRQ_MIN_PRIORITY 0 | ||
| 29 | #define AT91_AIC_IRQ_MAX_PRIORITY 7 | ||
| 30 | |||
| 31 | #define AT91_AIC_SRCTYPE GENMASK(7, 6) | ||
| 32 | #define AT91_AIC_SRCTYPE_LOW (0 << 5) | ||
| 33 | #define AT91_AIC_SRCTYPE_FALLING (1 << 5) | ||
| 34 | #define AT91_AIC_SRCTYPE_HIGH (2 << 5) | ||
| 35 | #define AT91_AIC_SRCTYPE_RISING (3 << 5) | ||
| 36 | |||
| 37 | struct aic_chip_data { | ||
| 38 | u32 ext_irqs; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static void aic_common_shutdown(struct irq_data *d) | ||
| 42 | { | ||
| 43 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
| 44 | |||
| 45 | ct->chip.irq_mask(d); | ||
| 46 | } | ||
| 47 | |||
| 48 | int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val) | ||
| 49 | { | ||
| 50 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 51 | struct aic_chip_data *aic = gc->private; | ||
| 52 | unsigned aic_type; | ||
| 53 | |||
| 54 | switch (type) { | ||
| 55 | case IRQ_TYPE_LEVEL_HIGH: | ||
| 56 | aic_type = AT91_AIC_SRCTYPE_HIGH; | ||
| 57 | break; | ||
| 58 | case IRQ_TYPE_EDGE_RISING: | ||
| 59 | aic_type = AT91_AIC_SRCTYPE_RISING; | ||
| 60 | break; | ||
| 61 | case IRQ_TYPE_LEVEL_LOW: | ||
| 62 | if (!(d->mask & aic->ext_irqs)) | ||
| 63 | return -EINVAL; | ||
| 64 | |||
| 65 | aic_type = AT91_AIC_SRCTYPE_LOW; | ||
| 66 | break; | ||
| 67 | case IRQ_TYPE_EDGE_FALLING: | ||
| 68 | if (!(d->mask & aic->ext_irqs)) | ||
| 69 | return -EINVAL; | ||
| 70 | |||
| 71 | aic_type = AT91_AIC_SRCTYPE_FALLING; | ||
| 72 | break; | ||
| 73 | default: | ||
| 74 | return -EINVAL; | ||
| 75 | } | ||
| 76 | |||
| 77 | *val &= AT91_AIC_SRCTYPE; | ||
| 78 | *val |= aic_type; | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | int aic_common_set_priority(int priority, unsigned *val) | ||
| 84 | { | ||
| 85 | if (priority < AT91_AIC_IRQ_MIN_PRIORITY || | ||
| 86 | priority > AT91_AIC_IRQ_MAX_PRIORITY) | ||
| 87 | return -EINVAL; | ||
| 88 | |||
| 89 | *val &= AT91_AIC_PRIOR; | ||
| 90 | *val |= priority; | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | int aic_common_irq_domain_xlate(struct irq_domain *d, | ||
| 96 | struct device_node *ctrlr, | ||
| 97 | const u32 *intspec, | ||
| 98 | unsigned int intsize, | ||
| 99 | irq_hw_number_t *out_hwirq, | ||
| 100 | unsigned int *out_type) | ||
| 101 | { | ||
| 102 | if (WARN_ON(intsize < 3)) | ||
| 103 | return -EINVAL; | ||
| 104 | |||
| 105 | if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) || | ||
| 106 | (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY))) | ||
| 107 | return -EINVAL; | ||
| 108 | |||
| 109 | *out_hwirq = intspec[0]; | ||
| 110 | *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) | ||
| 116 | { | ||
| 117 | struct device_node *node = domain->of_node; | ||
| 118 | struct irq_chip_generic *gc; | ||
| 119 | struct aic_chip_data *aic; | ||
| 120 | struct property *prop; | ||
| 121 | const __be32 *p; | ||
| 122 | u32 hwirq; | ||
| 123 | |||
| 124 | gc = irq_get_domain_generic_chip(domain, 0); | ||
| 125 | |||
| 126 | aic = gc->private; | ||
| 127 | aic->ext_irqs |= 1; | ||
| 128 | |||
| 129 | of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) { | ||
| 130 | gc = irq_get_domain_generic_chip(domain, hwirq); | ||
| 131 | if (!gc) { | ||
| 132 | pr_warn("AIC: external irq %d >= %d skip it\n", | ||
| 133 | hwirq, domain->revmap_size); | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | |||
| 137 | aic = gc->private; | ||
| 138 | aic->ext_irqs |= (1 << (hwirq % 32)); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | #define AT91_RTC_IDR 0x24 | ||
| 143 | #define AT91_RTC_IMR 0x28 | ||
| 144 | #define AT91_RTC_IRQ_MASK 0x1f | ||
| 145 | |||
| 146 | void __init aic_common_rtc_irq_fixup(struct device_node *root) | ||
| 147 | { | ||
| 148 | struct device_node *np; | ||
| 149 | void __iomem *regs; | ||
| 150 | |||
| 151 | np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc"); | ||
| 152 | if (!np) | ||
| 153 | np = of_find_compatible_node(root, NULL, | ||
| 154 | "atmel,at91sam9x5-rtc"); | ||
| 155 | |||
| 156 | if (!np) | ||
| 157 | return; | ||
| 158 | |||
| 159 | regs = of_iomap(np, 0); | ||
| 160 | of_node_put(np); | ||
| 161 | |||
| 162 | if (!regs) | ||
| 163 | return; | ||
| 164 | |||
| 165 | writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR); | ||
| 166 | |||
| 167 | iounmap(regs); | ||
| 168 | } | ||
| 169 | |||
| 170 | void __init aic_common_irq_fixup(const struct of_device_id *matches) | ||
| 171 | { | ||
| 172 | struct device_node *root = of_find_node_by_path("/"); | ||
| 173 | const struct of_device_id *match; | ||
| 174 | |||
| 175 | if (!root) | ||
| 176 | return; | ||
| 177 | |||
| 178 | match = of_match_node(matches, root); | ||
| 179 | of_node_put(root); | ||
| 180 | |||
| 181 | if (match) { | ||
| 182 | void (*fixup)(struct device_node *) = match->data; | ||
| 183 | fixup(root); | ||
| 184 | } | ||
| 185 | |||
| 186 | of_node_put(root); | ||
| 187 | } | ||
| 188 | |||
| 189 | struct irq_domain *__init aic_common_of_init(struct device_node *node, | ||
| 190 | const struct irq_domain_ops *ops, | ||
| 191 | const char *name, int nirqs) | ||
| 192 | { | ||
| 193 | struct irq_chip_generic *gc; | ||
| 194 | struct irq_domain *domain; | ||
| 195 | struct aic_chip_data *aic; | ||
| 196 | void __iomem *reg_base; | ||
| 197 | int nchips; | ||
| 198 | int ret; | ||
| 199 | int i; | ||
| 200 | |||
| 201 | nchips = DIV_ROUND_UP(nirqs, 32); | ||
| 202 | |||
| 203 | reg_base = of_iomap(node, 0); | ||
| 204 | if (!reg_base) | ||
| 205 | return ERR_PTR(-ENOMEM); | ||
| 206 | |||
| 207 | aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL); | ||
| 208 | if (!aic) { | ||
| 209 | ret = -ENOMEM; | ||
| 210 | goto err_iounmap; | ||
| 211 | } | ||
| 212 | |||
| 213 | domain = irq_domain_add_linear(node, nchips * 32, ops, aic); | ||
| 214 | if (!domain) { | ||
| 215 | ret = -ENOMEM; | ||
| 216 | goto err_free_aic; | ||
| 217 | } | ||
| 218 | |||
| 219 | ret = irq_alloc_domain_generic_chips(domain, 32, 1, name, | ||
| 220 | handle_level_irq, 0, 0, | ||
| 221 | IRQCHIP_SKIP_SET_WAKE); | ||
| 222 | if (ret) | ||
| 223 | goto err_domain_remove; | ||
| 224 | |||
| 225 | for (i = 0; i < nchips; i++) { | ||
| 226 | gc = irq_get_domain_generic_chip(domain, i * 32); | ||
| 227 | |||
| 228 | gc->reg_base = reg_base; | ||
| 229 | |||
| 230 | gc->unused = 0; | ||
| 231 | gc->wake_enabled = ~0; | ||
| 232 | gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK; | ||
| 233 | gc->chip_types[0].handler = handle_fasteoi_irq; | ||
| 234 | gc->chip_types[0].chip.irq_eoi = irq_gc_eoi; | ||
| 235 | gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; | ||
| 236 | gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown; | ||
| 237 | gc->private = &aic[i]; | ||
| 238 | } | ||
| 239 | |||
| 240 | aic_common_ext_irq_of_init(domain); | ||
| 241 | |||
| 242 | return domain; | ||
| 243 | |||
| 244 | err_domain_remove: | ||
| 245 | irq_domain_remove(domain); | ||
| 246 | |||
| 247 | err_free_aic: | ||
| 248 | kfree(aic); | ||
| 249 | |||
| 250 | err_iounmap: | ||
| 251 | iounmap(reg_base); | ||
| 252 | |||
| 253 | return ERR_PTR(ret); | ||
| 254 | } | ||
diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h new file mode 100644 index 000000000000..90aa00e918d6 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic-common.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | * Atmel AT91 common AIC (Advanced Interrupt Controller) header file | ||
| 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 | #ifndef __IRQ_ATMEL_AIC_COMMON_H | ||
| 17 | #define __IRQ_ATMEL_AIC_COMMON_H | ||
| 18 | |||
| 19 | |||
| 20 | int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val); | ||
| 21 | |||
| 22 | int aic_common_set_priority(int priority, unsigned *val); | ||
| 23 | |||
| 24 | int aic_common_irq_domain_xlate(struct irq_domain *d, | ||
| 25 | struct device_node *ctrlr, | ||
| 26 | const u32 *intspec, | ||
| 27 | unsigned int intsize, | ||
| 28 | irq_hw_number_t *out_hwirq, | ||
| 29 | unsigned int *out_type); | ||
| 30 | |||
| 31 | struct irq_domain *__init aic_common_of_init(struct device_node *node, | ||
| 32 | const struct irq_domain_ops *ops, | ||
| 33 | const char *name, int nirqs); | ||
| 34 | |||
| 35 | void __init aic_common_rtc_irq_fixup(struct device_node *root); | ||
| 36 | |||
| 37 | void __init aic_common_irq_fixup(const struct of_device_id *matches); | ||
| 38 | |||
| 39 | #endif /* __IRQ_ATMEL_AIC_COMMON_H */ | ||
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c new file mode 100644 index 000000000000..a82869e9fb26 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic.c | |||
| @@ -0,0 +1,262 @@ | |||
| 1 | /* | ||
| 2 | * Atmel AT91 AIC (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/of.h> | ||
| 23 | #include <linux/of_address.h> | ||
| 24 | #include <linux/of_irq.h> | ||
| 25 | #include <linux/irqdomain.h> | ||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/io.h> | ||
| 29 | |||
| 30 | #include <asm/exception.h> | ||
| 31 | #include <asm/mach/irq.h> | ||
| 32 | |||
| 33 | #include "irq-atmel-aic-common.h" | ||
| 34 | #include "irqchip.h" | ||
| 35 | |||
| 36 | /* Number of irq lines managed by AIC */ | ||
| 37 | #define NR_AIC_IRQS 32 | ||
| 38 | |||
| 39 | #define AT91_AIC_SMR(n) ((n) * 4) | ||
| 40 | |||
| 41 | #define AT91_AIC_SVR(n) (0x80 + ((n) * 4)) | ||
| 42 | #define AT91_AIC_IVR 0x100 | ||
| 43 | #define AT91_AIC_FVR 0x104 | ||
| 44 | #define AT91_AIC_ISR 0x108 | ||
| 45 | |||
| 46 | #define AT91_AIC_IPR 0x10c | ||
| 47 | #define AT91_AIC_IMR 0x110 | ||
| 48 | #define AT91_AIC_CISR 0x114 | ||
| 49 | |||
| 50 | #define AT91_AIC_IECR 0x120 | ||
| 51 | #define AT91_AIC_IDCR 0x124 | ||
| 52 | #define AT91_AIC_ICCR 0x128 | ||
| 53 | #define AT91_AIC_ISCR 0x12c | ||
| 54 | #define AT91_AIC_EOICR 0x130 | ||
| 55 | #define AT91_AIC_SPU 0x134 | ||
| 56 | #define AT91_AIC_DCR 0x138 | ||
| 57 | |||
| 58 | static struct irq_domain *aic_domain; | ||
| 59 | |||
| 60 | static asmlinkage void __exception_irq_entry | ||
| 61 | aic_handle(struct pt_regs *regs) | ||
| 62 | { | ||
| 63 | struct irq_domain_chip_generic *dgc = aic_domain->gc; | ||
| 64 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 65 | u32 irqnr; | ||
| 66 | u32 irqstat; | ||
| 67 | |||
| 68 | irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR); | ||
| 69 | irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR); | ||
| 70 | |||
| 71 | irqnr = irq_find_mapping(aic_domain, irqnr); | ||
| 72 | |||
| 73 | if (!irqstat) | ||
| 74 | irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); | ||
| 75 | else | ||
| 76 | handle_IRQ(irqnr, regs); | ||
| 77 | } | ||
| 78 | |||
| 79 | static int aic_retrigger(struct irq_data *d) | ||
| 80 | { | ||
| 81 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 82 | |||
| 83 | /* Enable interrupt on AIC5 */ | ||
| 84 | irq_gc_lock(gc); | ||
| 85 | irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR); | ||
| 86 | irq_gc_unlock(gc); | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int aic_set_type(struct irq_data *d, unsigned type) | ||
| 92 | { | ||
| 93 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 94 | unsigned int smr; | ||
| 95 | int ret; | ||
| 96 | |||
| 97 | smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq)); | ||
| 98 | ret = aic_common_set_type(d, type, &smr); | ||
| 99 | if (ret) | ||
| 100 | return ret; | ||
| 101 | |||
| 102 | irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq)); | ||
| 103 | |||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | #ifdef CONFIG_PM | ||
| 108 | static void aic_suspend(struct irq_data *d) | ||
| 109 | { | ||
| 110 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 111 | |||
| 112 | irq_gc_lock(gc); | ||
| 113 | irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR); | ||
| 114 | irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR); | ||
| 115 | irq_gc_unlock(gc); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void aic_resume(struct irq_data *d) | ||
| 119 | { | ||
| 120 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 121 | |||
| 122 | irq_gc_lock(gc); | ||
| 123 | irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR); | ||
| 124 | irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR); | ||
| 125 | irq_gc_unlock(gc); | ||
| 126 | } | ||
| 127 | |||
| 128 | static void aic_pm_shutdown(struct irq_data *d) | ||
| 129 | { | ||
| 130 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 131 | |||
| 132 | irq_gc_lock(gc); | ||
| 133 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); | ||
| 134 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); | ||
| 135 | irq_gc_unlock(gc); | ||
| 136 | } | ||
| 137 | #else | ||
| 138 | #define aic_suspend NULL | ||
| 139 | #define aic_resume NULL | ||
| 140 | #define aic_pm_shutdown NULL | ||
| 141 | #endif /* CONFIG_PM */ | ||
| 142 | |||
| 143 | static void __init aic_hw_init(struct irq_domain *domain) | ||
| 144 | { | ||
| 145 | struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); | ||
| 146 | int i; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Perform 8 End Of Interrupt Command to make sure AIC | ||
| 150 | * will not Lock out nIRQ | ||
| 151 | */ | ||
| 152 | for (i = 0; i < 8; i++) | ||
| 153 | irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); | ||
| 154 | |||
| 155 | /* | ||
| 156 | * Spurious Interrupt ID in Spurious Vector Register. | ||
| 157 | * When there is no current interrupt, the IRQ Vector Register | ||
| 158 | * reads the value stored in AIC_SPU | ||
| 159 | */ | ||
| 160 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU); | ||
| 161 | |||
| 162 | /* No debugging in AIC: Debug (Protect) Control Register */ | ||
| 163 | irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR); | ||
| 164 | |||
| 165 | /* Disable and clear all interrupts initially */ | ||
| 166 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); | ||
| 167 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); | ||
| 168 | |||
| 169 | for (i = 0; i < 32; i++) | ||
| 170 | irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i)); | ||
| 171 | } | ||
| 172 | |||
| 173 | static int aic_irq_domain_xlate(struct irq_domain *d, | ||
| 174 | struct device_node *ctrlr, | ||
| 175 | const u32 *intspec, unsigned int intsize, | ||
| 176 | irq_hw_number_t *out_hwirq, | ||
| 177 | unsigned int *out_type) | ||
| 178 | { | ||
| 179 | struct irq_domain_chip_generic *dgc = d->gc; | ||
| 180 | struct irq_chip_generic *gc; | ||
| 181 | unsigned smr; | ||
| 182 | int idx; | ||
| 183 | int ret; | ||
| 184 | |||
| 185 | if (!dgc) | ||
| 186 | return -EINVAL; | ||
| 187 | |||
| 188 | ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize, | ||
| 189 | out_hwirq, out_type); | ||
| 190 | if (ret) | ||
| 191 | return ret; | ||
| 192 | |||
| 193 | idx = intspec[0] / dgc->irqs_per_chip; | ||
| 194 | if (idx >= dgc->num_chips) | ||
| 195 | return -EINVAL; | ||
| 196 | |||
| 197 | gc = dgc->gc[idx]; | ||
| 198 | |||
| 199 | irq_gc_lock(gc); | ||
| 200 | smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq)); | ||
| 201 | ret = aic_common_set_priority(intspec[2], &smr); | ||
| 202 | if (!ret) | ||
| 203 | irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq)); | ||
| 204 | irq_gc_unlock(gc); | ||
| 205 | |||
| 206 | return ret; | ||
| 207 | } | ||
| 208 | |||
| 209 | static const struct irq_domain_ops aic_irq_ops = { | ||
| 210 | .map = irq_map_generic_chip, | ||
| 211 | .xlate = aic_irq_domain_xlate, | ||
| 212 | }; | ||
| 213 | |||
| 214 | static void __init at91sam9_aic_irq_fixup(struct device_node *root) | ||
| 215 | { | ||
| 216 | aic_common_rtc_irq_fixup(root); | ||
| 217 | } | ||
| 218 | |||
| 219 | static const struct of_device_id __initdata aic_irq_fixups[] = { | ||
| 220 | { .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup }, | ||
| 221 | { .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup }, | ||
| 222 | { .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup }, | ||
| 223 | { .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup }, | ||
| 224 | { /* sentinel */ }, | ||
| 225 | }; | ||
| 226 | |||
| 227 | static int __init aic_of_init(struct device_node *node, | ||
| 228 | struct device_node *parent) | ||
| 229 | { | ||
| 230 | struct irq_chip_generic *gc; | ||
| 231 | struct irq_domain *domain; | ||
| 232 | |||
| 233 | if (aic_domain) | ||
| 234 | return -EEXIST; | ||
| 235 | |||
| 236 | domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic", | ||
| 237 | NR_AIC_IRQS); | ||
| 238 | if (IS_ERR(domain)) | ||
| 239 | return PTR_ERR(domain); | ||
| 240 | |||
| 241 | aic_common_irq_fixup(aic_irq_fixups); | ||
| 242 | |||
| 243 | aic_domain = domain; | ||
| 244 | gc = irq_get_domain_generic_chip(domain, 0); | ||
| 245 | |||
| 246 | gc->chip_types[0].regs.eoi = AT91_AIC_EOICR; | ||
| 247 | gc->chip_types[0].regs.enable = AT91_AIC_IECR; | ||
| 248 | gc->chip_types[0].regs.disable = AT91_AIC_IDCR; | ||
| 249 | gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; | ||
| 250 | gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; | ||
| 251 | gc->chip_types[0].chip.irq_retrigger = aic_retrigger; | ||
| 252 | gc->chip_types[0].chip.irq_set_type = aic_set_type; | ||
| 253 | gc->chip_types[0].chip.irq_suspend = aic_suspend; | ||
| 254 | gc->chip_types[0].chip.irq_resume = aic_resume; | ||
| 255 | gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown; | ||
| 256 | |||
| 257 | aic_hw_init(domain); | ||
| 258 | set_handle_irq(aic_handle); | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init); | ||
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c new file mode 100644 index 000000000000..edb227081524 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic5.c | |||
| @@ -0,0 +1,353 @@ | |||
| 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/of.h> | ||
| 23 | #include <linux/of_address.h> | ||
| 24 | #include <linux/of_irq.h> | ||
| 25 | #include <linux/irqdomain.h> | ||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/io.h> | ||
| 29 | |||
| 30 | #include <asm/exception.h> | ||
| 31 | #include <asm/mach/irq.h> | ||
| 32 | |||
| 33 | #include "irq-atmel-aic-common.h" | ||
| 34 | #include "irqchip.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 | |||
| 68 | static struct irq_domain *aic5_domain; | ||
| 69 | |||
| 70 | static asmlinkage void __exception_irq_entry | ||
| 71 | aic5_handle(struct pt_regs *regs) | ||
| 72 | { | ||
| 73 | struct irq_domain_chip_generic *dgc = aic5_domain->gc; | ||
| 74 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 75 | u32 irqnr; | ||
| 76 | u32 irqstat; | ||
| 77 | |||
| 78 | irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR); | ||
| 79 | irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR); | ||
| 80 | |||
| 81 | irqnr = irq_find_mapping(aic5_domain, irqnr); | ||
| 82 | |||
| 83 | if (!irqstat) | ||
| 84 | irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); | ||
| 85 | else | ||
| 86 | handle_IRQ(irqnr, regs); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void aic5_mask(struct irq_data *d) | ||
| 90 | { | ||
| 91 | struct irq_domain *domain = d->domain; | ||
| 92 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 93 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 94 | |||
| 95 | /* Disable interrupt on AIC5 */ | ||
| 96 | irq_gc_lock(gc); | ||
| 97 | irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); | ||
| 98 | irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); | ||
| 99 | gc->mask_cache &= ~d->mask; | ||
| 100 | irq_gc_unlock(gc); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void aic5_unmask(struct irq_data *d) | ||
| 104 | { | ||
| 105 | struct irq_domain *domain = d->domain; | ||
| 106 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 107 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 108 | |||
| 109 | /* Enable interrupt on AIC5 */ | ||
| 110 | irq_gc_lock(gc); | ||
| 111 | irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); | ||
| 112 | irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR); | ||
| 113 | gc->mask_cache |= d->mask; | ||
| 114 | irq_gc_unlock(gc); | ||
| 115 | } | ||
| 116 | |||
| 117 | static int aic5_retrigger(struct irq_data *d) | ||
| 118 | { | ||
| 119 | struct irq_domain *domain = d->domain; | ||
| 120 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 121 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 122 | |||
| 123 | /* Enable interrupt on AIC5 */ | ||
| 124 | irq_gc_lock(gc); | ||
| 125 | irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); | ||
| 126 | irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR); | ||
| 127 | irq_gc_unlock(gc); | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int aic5_set_type(struct irq_data *d, unsigned type) | ||
| 133 | { | ||
| 134 | struct irq_domain *domain = d->domain; | ||
| 135 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 136 | struct irq_chip_generic *gc = dgc->gc[0]; | ||
| 137 | unsigned int smr; | ||
| 138 | int ret; | ||
| 139 | |||
| 140 | irq_gc_lock(gc); | ||
| 141 | irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); | ||
| 142 | smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); | ||
| 143 | ret = aic_common_set_type(d, type, &smr); | ||
| 144 | if (!ret) | ||
| 145 | irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR); | ||
| 146 | irq_gc_unlock(gc); | ||
| 147 | |||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | #ifdef CONFIG_PM | ||
| 152 | static void aic5_suspend(struct irq_data *d) | ||
| 153 | { | ||
| 154 | struct irq_domain *domain = d->domain; | ||
| 155 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 156 | struct irq_chip_generic *bgc = dgc->gc[0]; | ||
| 157 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 158 | int i; | ||
| 159 | u32 mask; | ||
| 160 | |||
| 161 | irq_gc_lock(bgc); | ||
| 162 | for (i = 0; i < dgc->irqs_per_chip; i++) { | ||
| 163 | mask = 1 << i; | ||
| 164 | if ((mask & gc->mask_cache) == (mask & gc->wake_active)) | ||
| 165 | continue; | ||
| 166 | |||
| 167 | irq_reg_writel(i + gc->irq_base, | ||
| 168 | bgc->reg_base + AT91_AIC5_SSR); | ||
| 169 | if (mask & gc->wake_active) | ||
| 170 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); | ||
| 171 | else | ||
| 172 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); | ||
| 173 | } | ||
| 174 | irq_gc_unlock(bgc); | ||
| 175 | } | ||
| 176 | |||
| 177 | static 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 = dgc->gc[0]; | ||
| 182 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 183 | int i; | ||
| 184 | u32 mask; | ||
| 185 | |||
| 186 | irq_gc_lock(bgc); | ||
| 187 | for (i = 0; i < dgc->irqs_per_chip; i++) { | ||
| 188 | mask = 1 << i; | ||
| 189 | if ((mask & gc->mask_cache) == (mask & gc->wake_active)) | ||
| 190 | continue; | ||
| 191 | |||
| 192 | irq_reg_writel(i + gc->irq_base, | ||
| 193 | bgc->reg_base + AT91_AIC5_SSR); | ||
| 194 | if (mask & gc->mask_cache) | ||
| 195 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); | ||
| 196 | else | ||
| 197 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); | ||
| 198 | } | ||
| 199 | irq_gc_unlock(bgc); | ||
| 200 | } | ||
| 201 | |||
| 202 | static void aic5_pm_shutdown(struct irq_data *d) | ||
| 203 | { | ||
| 204 | struct irq_domain *domain = d->domain; | ||
| 205 | struct irq_domain_chip_generic *dgc = domain->gc; | ||
| 206 | struct irq_chip_generic *bgc = dgc->gc[0]; | ||
| 207 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
| 208 | int i; | ||
| 209 | |||
| 210 | irq_gc_lock(bgc); | ||
| 211 | for (i = 0; i < dgc->irqs_per_chip; i++) { | ||
| 212 | irq_reg_writel(i + gc->irq_base, | ||
| 213 | bgc->reg_base + AT91_AIC5_SSR); | ||
| 214 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); | ||
| 215 | irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR); | ||
| 216 | } | ||
| 217 | irq_gc_unlock(bgc); | ||
| 218 | } | ||
| 219 | #else | ||
| 220 | #define aic5_suspend NULL | ||
| 221 | #define aic5_resume NULL | ||
| 222 | #define aic5_pm_shutdown NULL | ||
| 223 | #endif /* CONFIG_PM */ | ||
| 224 | |||
| 225 | static void __init aic5_hw_init(struct irq_domain *domain) | ||
| 226 | { | ||
| 227 | struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); | ||
| 228 | int i; | ||
| 229 | |||
| 230 | /* | ||
| 231 | * Perform 8 End Of Interrupt Command to make sure AIC | ||
| 232 | * will not Lock out nIRQ | ||
| 233 | */ | ||
| 234 | for (i = 0; i < 8; i++) | ||
| 235 | irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); | ||
| 236 | |||
| 237 | /* | ||
| 238 | * Spurious Interrupt ID in Spurious Vector Register. | ||
| 239 | * When there is no current interrupt, the IRQ Vector Register | ||
| 240 | * reads the value stored in AIC_SPU | ||
| 241 | */ | ||
| 242 | irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU); | ||
| 243 | |||
| 244 | /* No debugging in AIC: Debug (Protect) Control Register */ | ||
| 245 | irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR); | ||
| 246 | |||
| 247 | /* Disable and clear all interrupts initially */ | ||
| 248 | for (i = 0; i < domain->revmap_size; i++) { | ||
| 249 | irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR); | ||
| 250 | irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR); | ||
| 251 | irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); | ||
| 252 | irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | static int aic5_irq_domain_xlate(struct irq_domain *d, | ||
| 257 | struct device_node *ctrlr, | ||
| 258 | const u32 *intspec, unsigned int intsize, | ||
| 259 | irq_hw_number_t *out_hwirq, | ||
| 260 | unsigned int *out_type) | ||
| 261 | { | ||
| 262 | struct irq_domain_chip_generic *dgc = d->gc; | ||
| 263 | struct irq_chip_generic *gc; | ||
| 264 | unsigned smr; | ||
| 265 | int ret; | ||
| 266 | |||
| 267 | if (!dgc) | ||
| 268 | return -EINVAL; | ||
| 269 | |||
| 270 | ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize, | ||
| 271 | out_hwirq, out_type); | ||
| 272 | if (ret) | ||
| 273 | return ret; | ||
| 274 | |||
| 275 | gc = dgc->gc[0]; | ||
| 276 | |||
| 277 | irq_gc_lock(gc); | ||
| 278 | irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR); | ||
| 279 | smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); | ||
| 280 | ret = aic_common_set_priority(intspec[2], &smr); | ||
| 281 | if (!ret) | ||
| 282 | irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR); | ||
| 283 | irq_gc_unlock(gc); | ||
| 284 | |||
| 285 | return ret; | ||
| 286 | } | ||
| 287 | |||
| 288 | static const struct irq_domain_ops aic5_irq_ops = { | ||
| 289 | .map = irq_map_generic_chip, | ||
| 290 | .xlate = aic5_irq_domain_xlate, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static void __init sama5d3_aic_irq_fixup(struct device_node *root) | ||
| 294 | { | ||
| 295 | aic_common_rtc_irq_fixup(root); | ||
| 296 | } | ||
| 297 | |||
| 298 | static const struct of_device_id __initdata aic5_irq_fixups[] = { | ||
| 299 | { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup }, | ||
| 300 | { /* sentinel */ }, | ||
| 301 | }; | ||
| 302 | |||
| 303 | static int __init aic5_of_init(struct device_node *node, | ||
| 304 | struct device_node *parent, | ||
| 305 | int nirqs) | ||
| 306 | { | ||
| 307 | struct irq_chip_generic *gc; | ||
| 308 | struct irq_domain *domain; | ||
| 309 | int nchips; | ||
| 310 | int i; | ||
| 311 | |||
| 312 | if (nirqs > NR_AIC5_IRQS) | ||
| 313 | return -EINVAL; | ||
| 314 | |||
| 315 | if (aic5_domain) | ||
| 316 | return -EEXIST; | ||
| 317 | |||
| 318 | domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5", | ||
| 319 | nirqs); | ||
| 320 | if (IS_ERR(domain)) | ||
| 321 | return PTR_ERR(domain); | ||
| 322 | |||
| 323 | aic_common_irq_fixup(aic5_irq_fixups); | ||
| 324 | |||
| 325 | aic5_domain = domain; | ||
| 326 | nchips = aic5_domain->revmap_size / 32; | ||
| 327 | for (i = 0; i < nchips; i++) { | ||
| 328 | gc = irq_get_domain_generic_chip(domain, i * 32); | ||
| 329 | |||
| 330 | gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR; | ||
| 331 | gc->chip_types[0].chip.irq_mask = aic5_mask; | ||
| 332 | gc->chip_types[0].chip.irq_unmask = aic5_unmask; | ||
| 333 | gc->chip_types[0].chip.irq_retrigger = aic5_retrigger; | ||
| 334 | gc->chip_types[0].chip.irq_set_type = aic5_set_type; | ||
| 335 | gc->chip_types[0].chip.irq_suspend = aic5_suspend; | ||
| 336 | gc->chip_types[0].chip.irq_resume = aic5_resume; | ||
| 337 | gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown; | ||
| 338 | } | ||
| 339 | |||
| 340 | aic5_hw_init(domain); | ||
| 341 | set_handle_irq(aic5_handle); | ||
| 342 | |||
| 343 | return 0; | ||
| 344 | } | ||
| 345 | |||
| 346 | #define NR_SAMA5D3_IRQS 50 | ||
| 347 | |||
| 348 | static int __init sama5d3_aic5_of_init(struct device_node *node, | ||
| 349 | struct device_node *parent) | ||
| 350 | { | ||
| 351 | return aic5_of_init(node, parent, NR_SAMA5D3_IRQS); | ||
| 352 | } | ||
| 353 | IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init); | ||
diff --git a/include/linux/irq.h b/include/linux/irq.h index 0d998d8b01d8..62af59242ddc 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
| @@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d); | |||
| 771 | int irq_gc_set_wake(struct irq_data *d, unsigned int on); | 771 | int irq_gc_set_wake(struct irq_data *d, unsigned int on); |
| 772 | 772 | ||
| 773 | /* Setup functions for irq_chip_generic */ | 773 | /* Setup functions for irq_chip_generic */ |
| 774 | int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, | ||
| 775 | irq_hw_number_t hw_irq); | ||
| 774 | struct irq_chip_generic * | 776 | struct irq_chip_generic * |
| 775 | irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base, | 777 | irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base, |
| 776 | void __iomem *reg_base, irq_flow_handler_t handler); | 778 | void __iomem *reg_base, irq_flow_handler_t handler); |
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index 452d6f2ba21d..cf80e7b0ddab 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c | |||
| @@ -341,8 +341,8 @@ static struct lock_class_key irq_nested_lock_class; | |||
| 341 | /* | 341 | /* |
| 342 | * irq_map_generic_chip - Map a generic chip for an irq domain | 342 | * irq_map_generic_chip - Map a generic chip for an irq domain |
| 343 | */ | 343 | */ |
| 344 | static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, | 344 | int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, |
| 345 | irq_hw_number_t hw_irq) | 345 | irq_hw_number_t hw_irq) |
| 346 | { | 346 | { |
| 347 | struct irq_data *data = irq_get_irq_data(virq); | 347 | struct irq_data *data = irq_get_irq_data(virq); |
| 348 | struct irq_domain_chip_generic *dgc = d->gc; | 348 | struct irq_domain_chip_generic *dgc = d->gc; |
| @@ -394,6 +394,7 @@ static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, | |||
| 394 | irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); | 394 | irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); |
| 395 | return 0; | 395 | return 0; |
| 396 | } | 396 | } |
| 397 | EXPORT_SYMBOL_GPL(irq_map_generic_chip); | ||
| 397 | 398 | ||
| 398 | struct irq_domain_ops irq_generic_chip_ops = { | 399 | struct irq_domain_ops irq_generic_chip_ops = { |
| 399 | .map = irq_map_generic_chip, | 400 | .map = irq_map_generic_chip, |
