diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 8 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 4 | ||||
-rw-r--r-- | drivers/irqchip/exynos-combiner.c | 80 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-intc-irqpin.c | 547 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-irqc.c | 307 | ||||
-rw-r--r-- | drivers/irqchip/irq-s3c24xx.c | 1356 | ||||
-rw-r--r-- | drivers/irqchip/irq-vt8500.c | 259 |
7 files changed, 2543 insertions, 18 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969e5efe..4a33351c25dc 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -25,6 +25,14 @@ config ARM_VIC_NR | |||
25 | The maximum number of VICs available in the system, for | 25 | The maximum number of VICs available in the system, for |
26 | power management. | 26 | power management. |
27 | 27 | ||
28 | config RENESAS_INTC_IRQPIN | ||
29 | bool | ||
30 | select IRQ_DOMAIN | ||
31 | |||
32 | config RENESAS_IRQC | ||
33 | bool | ||
34 | select IRQ_DOMAIN | ||
35 | |||
28 | config VERSATILE_FPGA_IRQ | 36 | config VERSATILE_FPGA_IRQ |
29 | bool | 37 | bool |
30 | select IRQ_DOMAIN | 38 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 10ef57f35a6e..c28fcccf4a0d 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -3,6 +3,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o | |||
3 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o | 3 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o |
4 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o | 4 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o |
5 | obj-$(CONFIG_ARCH_MXS) += irq-mxs.o | 5 | obj-$(CONFIG_ARCH_MXS) += irq-mxs.o |
6 | obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o | ||
6 | obj-$(CONFIG_METAG) += irq-metag-ext.o | 7 | obj-$(CONFIG_METAG) += irq-metag-ext.o |
7 | obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o | 8 | obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o |
8 | obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o | 9 | obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o |
@@ -10,4 +11,7 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | |||
10 | obj-$(CONFIG_ARM_GIC) += irq-gic.o | 11 | obj-$(CONFIG_ARM_GIC) += irq-gic.o |
11 | obj-$(CONFIG_ARM_VIC) += irq-vic.o | 12 | obj-$(CONFIG_ARM_VIC) += irq-vic.o |
12 | obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o | 13 | obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o |
14 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o | ||
15 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o | ||
13 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 16 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o |
17 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | ||
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 6a5201351507..02492ab20d22 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c | |||
@@ -32,6 +32,7 @@ struct combiner_chip_data { | |||
32 | unsigned int irq_offset; | 32 | unsigned int irq_offset; |
33 | unsigned int irq_mask; | 33 | unsigned int irq_mask; |
34 | void __iomem *base; | 34 | void __iomem *base; |
35 | unsigned int parent_irq; | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | static struct irq_domain *combiner_irq_domain; | 38 | static struct irq_domain *combiner_irq_domain; |
@@ -88,22 +89,46 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) | |||
88 | chained_irq_exit(chip, desc); | 89 | chained_irq_exit(chip, desc); |
89 | } | 90 | } |
90 | 91 | ||
92 | #ifdef CONFIG_SMP | ||
93 | static int combiner_set_affinity(struct irq_data *d, | ||
94 | const struct cpumask *mask_val, bool force) | ||
95 | { | ||
96 | struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
97 | struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); | ||
98 | struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); | ||
99 | |||
100 | if (chip && chip->irq_set_affinity) | ||
101 | return chip->irq_set_affinity(data, mask_val, force); | ||
102 | else | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | #endif | ||
106 | |||
91 | static struct irq_chip combiner_chip = { | 107 | static struct irq_chip combiner_chip = { |
92 | .name = "COMBINER", | 108 | .name = "COMBINER", |
93 | .irq_mask = combiner_mask_irq, | 109 | .irq_mask = combiner_mask_irq, |
94 | .irq_unmask = combiner_unmask_irq, | 110 | .irq_unmask = combiner_unmask_irq, |
111 | #ifdef CONFIG_SMP | ||
112 | .irq_set_affinity = combiner_set_affinity, | ||
113 | #endif | ||
95 | }; | 114 | }; |
96 | 115 | ||
97 | static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) | 116 | static unsigned int max_combiner_nr(void) |
98 | { | 117 | { |
99 | unsigned int max_nr; | ||
100 | |||
101 | if (soc_is_exynos5250()) | 118 | if (soc_is_exynos5250()) |
102 | max_nr = EXYNOS5_MAX_COMBINER_NR; | 119 | return EXYNOS5_MAX_COMBINER_NR; |
120 | else if (soc_is_exynos4412()) | ||
121 | return EXYNOS4412_MAX_COMBINER_NR; | ||
122 | else if (soc_is_exynos4212()) | ||
123 | return EXYNOS4212_MAX_COMBINER_NR; | ||
103 | else | 124 | else |
104 | max_nr = EXYNOS4_MAX_COMBINER_NR; | 125 | return EXYNOS4210_MAX_COMBINER_NR; |
126 | } | ||
105 | 127 | ||
106 | if (combiner_nr >= max_nr) | 128 | static void __init combiner_cascade_irq(unsigned int combiner_nr, |
129 | unsigned int irq) | ||
130 | { | ||
131 | if (combiner_nr >= max_combiner_nr()) | ||
107 | BUG(); | 132 | BUG(); |
108 | if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) | 133 | if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) |
109 | BUG(); | 134 | BUG(); |
@@ -111,12 +136,13 @@ static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int i | |||
111 | } | 136 | } |
112 | 137 | ||
113 | static void __init combiner_init_one(unsigned int combiner_nr, | 138 | static void __init combiner_init_one(unsigned int combiner_nr, |
114 | void __iomem *base) | 139 | void __iomem *base, unsigned int irq) |
115 | { | 140 | { |
116 | combiner_data[combiner_nr].base = base; | 141 | combiner_data[combiner_nr].base = base; |
117 | combiner_data[combiner_nr].irq_offset = irq_find_mapping( | 142 | combiner_data[combiner_nr].irq_offset = irq_find_mapping( |
118 | combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); | 143 | combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); |
119 | combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); | 144 | combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); |
145 | combiner_data[combiner_nr].parent_irq = irq; | ||
120 | 146 | ||
121 | /* Disable all interrupts */ | 147 | /* Disable all interrupts */ |
122 | __raw_writel(combiner_data[combiner_nr].irq_mask, | 148 | __raw_writel(combiner_data[combiner_nr].irq_mask, |
@@ -167,23 +193,38 @@ static struct irq_domain_ops combiner_irq_domain_ops = { | |||
167 | .map = combiner_irq_domain_map, | 193 | .map = combiner_irq_domain_map, |
168 | }; | 194 | }; |
169 | 195 | ||
196 | static unsigned int exynos4x12_combiner_extra_irq(int group) | ||
197 | { | ||
198 | switch (group) { | ||
199 | case 16: | ||
200 | return IRQ_SPI(107); | ||
201 | case 17: | ||
202 | return IRQ_SPI(108); | ||
203 | case 18: | ||
204 | return IRQ_SPI(48); | ||
205 | case 19: | ||
206 | return IRQ_SPI(42); | ||
207 | default: | ||
208 | return 0; | ||
209 | } | ||
210 | } | ||
211 | |||
170 | void __init combiner_init(void __iomem *combiner_base, | 212 | void __init combiner_init(void __iomem *combiner_base, |
171 | struct device_node *np) | 213 | struct device_node *np) |
172 | { | 214 | { |
173 | int i, irq, irq_base; | 215 | int i, irq, irq_base; |
174 | unsigned int max_nr, nr_irq; | 216 | unsigned int max_nr, nr_irq; |
175 | 217 | ||
218 | max_nr = max_combiner_nr(); | ||
219 | |||
176 | if (np) { | 220 | if (np) { |
177 | if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { | 221 | if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { |
178 | pr_warning("%s: number of combiners not specified, " | 222 | pr_info("%s: number of combiners not specified, " |
179 | "setting default as %d.\n", | 223 | "setting default as %d.\n", |
180 | __func__, EXYNOS4_MAX_COMBINER_NR); | 224 | __func__, max_nr); |
181 | max_nr = EXYNOS4_MAX_COMBINER_NR; | ||
182 | } | 225 | } |
183 | } else { | ||
184 | max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : | ||
185 | EXYNOS4_MAX_COMBINER_NR; | ||
186 | } | 226 | } |
227 | |||
187 | nr_irq = max_nr * MAX_IRQ_IN_COMBINER; | 228 | nr_irq = max_nr * MAX_IRQ_IN_COMBINER; |
188 | 229 | ||
189 | irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); | 230 | irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); |
@@ -200,12 +241,15 @@ void __init combiner_init(void __iomem *combiner_base, | |||
200 | } | 241 | } |
201 | 242 | ||
202 | for (i = 0; i < max_nr; i++) { | 243 | for (i = 0; i < max_nr; i++) { |
203 | combiner_init_one(i, combiner_base + (i >> 2) * 0x10); | 244 | if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250()) |
204 | irq = IRQ_SPI(i); | 245 | irq = IRQ_SPI(i); |
246 | else | ||
247 | irq = exynos4x12_combiner_extra_irq(i); | ||
205 | #ifdef CONFIG_OF | 248 | #ifdef CONFIG_OF |
206 | if (np) | 249 | if (np) |
207 | irq = irq_of_parse_and_map(np, i); | 250 | irq = irq_of_parse_and_map(np, i); |
208 | #endif | 251 | #endif |
252 | combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); | ||
209 | combiner_cascade_irq(i, irq); | 253 | combiner_cascade_irq(i, irq); |
210 | } | 254 | } |
211 | } | 255 | } |
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 000000000000..5a68e5accec1 --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | * Renesas INTC External IRQ Pin Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Magnus Damm | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/irqdomain.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/platform_data/irq-renesas-intc-irqpin.h> | ||
32 | |||
33 | #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ | ||
34 | |||
35 | #define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ | ||
36 | #define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ | ||
37 | #define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ | ||
38 | #define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ | ||
39 | #define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ | ||
40 | #define INTC_IRQPIN_REG_NR 5 | ||
41 | |||
42 | /* INTC external IRQ PIN hardware register access: | ||
43 | * | ||
44 | * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) | ||
45 | * PRIO is read-write 32-bit with 4-bits per IRQ (**) | ||
46 | * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
47 | * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
48 | * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) | ||
49 | * | ||
50 | * (*) May be accessed by more than one driver instance - lock needed | ||
51 | * (**) Read-modify-write access by one driver instance - lock needed | ||
52 | * (***) Accessed by one driver instance only - no locking needed | ||
53 | */ | ||
54 | |||
55 | struct intc_irqpin_iomem { | ||
56 | void __iomem *iomem; | ||
57 | unsigned long (*read)(void __iomem *iomem); | ||
58 | void (*write)(void __iomem *iomem, unsigned long data); | ||
59 | int width; | ||
60 | }; | ||
61 | |||
62 | struct intc_irqpin_irq { | ||
63 | int hw_irq; | ||
64 | int requested_irq; | ||
65 | int domain_irq; | ||
66 | struct intc_irqpin_priv *p; | ||
67 | }; | ||
68 | |||
69 | struct intc_irqpin_priv { | ||
70 | struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; | ||
71 | struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; | ||
72 | struct renesas_intc_irqpin_config config; | ||
73 | unsigned int number_of_irqs; | ||
74 | struct platform_device *pdev; | ||
75 | struct irq_chip irq_chip; | ||
76 | struct irq_domain *irq_domain; | ||
77 | bool shared_irqs; | ||
78 | u8 shared_irq_mask; | ||
79 | }; | ||
80 | |||
81 | static unsigned long intc_irqpin_read32(void __iomem *iomem) | ||
82 | { | ||
83 | return ioread32(iomem); | ||
84 | } | ||
85 | |||
86 | static unsigned long intc_irqpin_read8(void __iomem *iomem) | ||
87 | { | ||
88 | return ioread8(iomem); | ||
89 | } | ||
90 | |||
91 | static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) | ||
92 | { | ||
93 | iowrite32(data, iomem); | ||
94 | } | ||
95 | |||
96 | static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) | ||
97 | { | ||
98 | iowrite8(data, iomem); | ||
99 | } | ||
100 | |||
101 | static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, | ||
102 | int reg) | ||
103 | { | ||
104 | struct intc_irqpin_iomem *i = &p->iomem[reg]; | ||
105 | |||
106 | return i->read(i->iomem); | ||
107 | } | ||
108 | |||
109 | static inline void intc_irqpin_write(struct intc_irqpin_priv *p, | ||
110 | int reg, unsigned long data) | ||
111 | { | ||
112 | struct intc_irqpin_iomem *i = &p->iomem[reg]; | ||
113 | |||
114 | i->write(i->iomem, data); | ||
115 | } | ||
116 | |||
117 | static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, | ||
118 | int reg, int hw_irq) | ||
119 | { | ||
120 | return BIT((p->iomem[reg].width - 1) - hw_irq); | ||
121 | } | ||
122 | |||
123 | static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, | ||
124 | int reg, int hw_irq) | ||
125 | { | ||
126 | intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); | ||
127 | } | ||
128 | |||
129 | static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ | ||
130 | |||
131 | static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, | ||
132 | int reg, int shift, | ||
133 | int width, int value) | ||
134 | { | ||
135 | unsigned long flags; | ||
136 | unsigned long tmp; | ||
137 | |||
138 | raw_spin_lock_irqsave(&intc_irqpin_lock, flags); | ||
139 | |||
140 | tmp = intc_irqpin_read(p, reg); | ||
141 | tmp &= ~(((1 << width) - 1) << shift); | ||
142 | tmp |= value << shift; | ||
143 | intc_irqpin_write(p, reg, tmp); | ||
144 | |||
145 | raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); | ||
146 | } | ||
147 | |||
148 | static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, | ||
149 | int irq, int do_mask) | ||
150 | { | ||
151 | int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ | ||
152 | int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ | ||
153 | |||
154 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, | ||
155 | shift, bitfield_width, | ||
156 | do_mask ? 0 : (1 << bitfield_width) - 1); | ||
157 | } | ||
158 | |||
159 | static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) | ||
160 | { | ||
161 | int bitfield_width = p->config.sense_bitfield_width; | ||
162 | int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ | ||
163 | |||
164 | dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); | ||
165 | |||
166 | if (value >= (1 << bitfield_width)) | ||
167 | return -EINVAL; | ||
168 | |||
169 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, | ||
170 | bitfield_width, value); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) | ||
175 | { | ||
176 | dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", | ||
177 | str, i->requested_irq, i->hw_irq, i->domain_irq); | ||
178 | } | ||
179 | |||
180 | static void intc_irqpin_irq_enable(struct irq_data *d) | ||
181 | { | ||
182 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
183 | int hw_irq = irqd_to_hwirq(d); | ||
184 | |||
185 | intc_irqpin_dbg(&p->irq[hw_irq], "enable"); | ||
186 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); | ||
187 | } | ||
188 | |||
189 | static void intc_irqpin_irq_disable(struct irq_data *d) | ||
190 | { | ||
191 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
192 | int hw_irq = irqd_to_hwirq(d); | ||
193 | |||
194 | intc_irqpin_dbg(&p->irq[hw_irq], "disable"); | ||
195 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); | ||
196 | } | ||
197 | |||
198 | static void intc_irqpin_shared_irq_enable(struct irq_data *d) | ||
199 | { | ||
200 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
201 | int hw_irq = irqd_to_hwirq(d); | ||
202 | |||
203 | intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); | ||
204 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); | ||
205 | |||
206 | p->shared_irq_mask &= ~BIT(hw_irq); | ||
207 | } | ||
208 | |||
209 | static void intc_irqpin_shared_irq_disable(struct irq_data *d) | ||
210 | { | ||
211 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
212 | int hw_irq = irqd_to_hwirq(d); | ||
213 | |||
214 | intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); | ||
215 | intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); | ||
216 | |||
217 | p->shared_irq_mask |= BIT(hw_irq); | ||
218 | } | ||
219 | |||
220 | static void intc_irqpin_irq_enable_force(struct irq_data *d) | ||
221 | { | ||
222 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
223 | int irq = p->irq[irqd_to_hwirq(d)].requested_irq; | ||
224 | |||
225 | intc_irqpin_irq_enable(d); | ||
226 | |||
227 | /* enable interrupt through parent interrupt controller, | ||
228 | * assumes non-shared interrupt with 1:1 mapping | ||
229 | * needed for busted IRQs on some SoCs like sh73a0 | ||
230 | */ | ||
231 | irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); | ||
232 | } | ||
233 | |||
234 | static void intc_irqpin_irq_disable_force(struct irq_data *d) | ||
235 | { | ||
236 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
237 | int irq = p->irq[irqd_to_hwirq(d)].requested_irq; | ||
238 | |||
239 | /* disable interrupt through parent interrupt controller, | ||
240 | * assumes non-shared interrupt with 1:1 mapping | ||
241 | * needed for busted IRQs on some SoCs like sh73a0 | ||
242 | */ | ||
243 | irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); | ||
244 | intc_irqpin_irq_disable(d); | ||
245 | } | ||
246 | |||
247 | #define INTC_IRQ_SENSE_VALID 0x10 | ||
248 | #define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) | ||
249 | |||
250 | static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { | ||
251 | [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), | ||
252 | [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), | ||
253 | [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), | ||
254 | [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), | ||
255 | [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), | ||
256 | }; | ||
257 | |||
258 | static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) | ||
259 | { | ||
260 | unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; | ||
261 | struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); | ||
262 | |||
263 | if (!(value & INTC_IRQ_SENSE_VALID)) | ||
264 | return -EINVAL; | ||
265 | |||
266 | return intc_irqpin_set_sense(p, irqd_to_hwirq(d), | ||
267 | value ^ INTC_IRQ_SENSE_VALID); | ||
268 | } | ||
269 | |||
270 | static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) | ||
271 | { | ||
272 | struct intc_irqpin_irq *i = dev_id; | ||
273 | struct intc_irqpin_priv *p = i->p; | ||
274 | unsigned long bit; | ||
275 | |||
276 | intc_irqpin_dbg(i, "demux1"); | ||
277 | bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); | ||
278 | |||
279 | if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { | ||
280 | intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); | ||
281 | intc_irqpin_dbg(i, "demux2"); | ||
282 | generic_handle_irq(i->domain_irq); | ||
283 | return IRQ_HANDLED; | ||
284 | } | ||
285 | return IRQ_NONE; | ||
286 | } | ||
287 | |||
288 | static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) | ||
289 | { | ||
290 | struct intc_irqpin_priv *p = dev_id; | ||
291 | unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); | ||
292 | irqreturn_t status = IRQ_NONE; | ||
293 | int k; | ||
294 | |||
295 | for (k = 0; k < 8; k++) { | ||
296 | if (reg_source & BIT(7 - k)) { | ||
297 | if (BIT(k) & p->shared_irq_mask) | ||
298 | continue; | ||
299 | |||
300 | status |= intc_irqpin_irq_handler(irq, &p->irq[k]); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | return status; | ||
305 | } | ||
306 | |||
307 | static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, | ||
308 | irq_hw_number_t hw) | ||
309 | { | ||
310 | struct intc_irqpin_priv *p = h->host_data; | ||
311 | |||
312 | p->irq[hw].domain_irq = virq; | ||
313 | p->irq[hw].hw_irq = hw; | ||
314 | |||
315 | intc_irqpin_dbg(&p->irq[hw], "map"); | ||
316 | irq_set_chip_data(virq, h->host_data); | ||
317 | irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); | ||
318 | set_irq_flags(virq, IRQF_VALID); /* kill me now */ | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static struct irq_domain_ops intc_irqpin_irq_domain_ops = { | ||
323 | .map = intc_irqpin_irq_domain_map, | ||
324 | .xlate = irq_domain_xlate_twocell, | ||
325 | }; | ||
326 | |||
327 | static int intc_irqpin_probe(struct platform_device *pdev) | ||
328 | { | ||
329 | struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; | ||
330 | struct intc_irqpin_priv *p; | ||
331 | struct intc_irqpin_iomem *i; | ||
332 | struct resource *io[INTC_IRQPIN_REG_NR]; | ||
333 | struct resource *irq; | ||
334 | struct irq_chip *irq_chip; | ||
335 | void (*enable_fn)(struct irq_data *d); | ||
336 | void (*disable_fn)(struct irq_data *d); | ||
337 | const char *name = dev_name(&pdev->dev); | ||
338 | int ref_irq; | ||
339 | int ret; | ||
340 | int k; | ||
341 | |||
342 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | ||
343 | if (!p) { | ||
344 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | ||
345 | ret = -ENOMEM; | ||
346 | goto err0; | ||
347 | } | ||
348 | |||
349 | /* deal with driver instance configuration */ | ||
350 | if (pdata) | ||
351 | memcpy(&p->config, pdata, sizeof(*pdata)); | ||
352 | if (!p->config.sense_bitfield_width) | ||
353 | p->config.sense_bitfield_width = 4; /* default to 4 bits */ | ||
354 | |||
355 | p->pdev = pdev; | ||
356 | platform_set_drvdata(pdev, p); | ||
357 | |||
358 | /* get hold of manadatory IOMEM */ | ||
359 | for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { | ||
360 | io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); | ||
361 | if (!io[k]) { | ||
362 | dev_err(&pdev->dev, "not enough IOMEM resources\n"); | ||
363 | ret = -EINVAL; | ||
364 | goto err0; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ | ||
369 | for (k = 0; k < INTC_IRQPIN_MAX; k++) { | ||
370 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); | ||
371 | if (!irq) | ||
372 | break; | ||
373 | |||
374 | p->irq[k].p = p; | ||
375 | p->irq[k].requested_irq = irq->start; | ||
376 | } | ||
377 | |||
378 | p->number_of_irqs = k; | ||
379 | if (p->number_of_irqs < 1) { | ||
380 | dev_err(&pdev->dev, "not enough IRQ resources\n"); | ||
381 | ret = -EINVAL; | ||
382 | goto err0; | ||
383 | } | ||
384 | |||
385 | /* ioremap IOMEM and setup read/write callbacks */ | ||
386 | for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { | ||
387 | i = &p->iomem[k]; | ||
388 | |||
389 | switch (resource_size(io[k])) { | ||
390 | case 1: | ||
391 | i->width = 8; | ||
392 | i->read = intc_irqpin_read8; | ||
393 | i->write = intc_irqpin_write8; | ||
394 | break; | ||
395 | case 4: | ||
396 | i->width = 32; | ||
397 | i->read = intc_irqpin_read32; | ||
398 | i->write = intc_irqpin_write32; | ||
399 | break; | ||
400 | default: | ||
401 | dev_err(&pdev->dev, "IOMEM size mismatch\n"); | ||
402 | ret = -EINVAL; | ||
403 | goto err0; | ||
404 | } | ||
405 | |||
406 | i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, | ||
407 | resource_size(io[k])); | ||
408 | if (!i->iomem) { | ||
409 | dev_err(&pdev->dev, "failed to remap IOMEM\n"); | ||
410 | ret = -ENXIO; | ||
411 | goto err0; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | /* mask all interrupts using priority */ | ||
416 | for (k = 0; k < p->number_of_irqs; k++) | ||
417 | intc_irqpin_mask_unmask_prio(p, k, 1); | ||
418 | |||
419 | /* clear all pending interrupts */ | ||
420 | intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); | ||
421 | |||
422 | /* scan for shared interrupt lines */ | ||
423 | ref_irq = p->irq[0].requested_irq; | ||
424 | p->shared_irqs = true; | ||
425 | for (k = 1; k < p->number_of_irqs; k++) { | ||
426 | if (ref_irq != p->irq[k].requested_irq) { | ||
427 | p->shared_irqs = false; | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | /* use more severe masking method if requested */ | ||
433 | if (p->config.control_parent) { | ||
434 | enable_fn = intc_irqpin_irq_enable_force; | ||
435 | disable_fn = intc_irqpin_irq_disable_force; | ||
436 | } else if (!p->shared_irqs) { | ||
437 | enable_fn = intc_irqpin_irq_enable; | ||
438 | disable_fn = intc_irqpin_irq_disable; | ||
439 | } else { | ||
440 | enable_fn = intc_irqpin_shared_irq_enable; | ||
441 | disable_fn = intc_irqpin_shared_irq_disable; | ||
442 | } | ||
443 | |||
444 | irq_chip = &p->irq_chip; | ||
445 | irq_chip->name = name; | ||
446 | irq_chip->irq_mask = disable_fn; | ||
447 | irq_chip->irq_unmask = enable_fn; | ||
448 | irq_chip->irq_enable = enable_fn; | ||
449 | irq_chip->irq_disable = disable_fn; | ||
450 | irq_chip->irq_set_type = intc_irqpin_irq_set_type; | ||
451 | irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; | ||
452 | |||
453 | p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, | ||
454 | p->number_of_irqs, | ||
455 | p->config.irq_base, | ||
456 | &intc_irqpin_irq_domain_ops, p); | ||
457 | if (!p->irq_domain) { | ||
458 | ret = -ENXIO; | ||
459 | dev_err(&pdev->dev, "cannot initialize irq domain\n"); | ||
460 | goto err0; | ||
461 | } | ||
462 | |||
463 | if (p->shared_irqs) { | ||
464 | /* request one shared interrupt */ | ||
465 | if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq, | ||
466 | intc_irqpin_shared_irq_handler, | ||
467 | IRQF_SHARED, name, p)) { | ||
468 | dev_err(&pdev->dev, "failed to request low IRQ\n"); | ||
469 | ret = -ENOENT; | ||
470 | goto err1; | ||
471 | } | ||
472 | } else { | ||
473 | /* request interrupts one by one */ | ||
474 | for (k = 0; k < p->number_of_irqs; k++) { | ||
475 | if (devm_request_irq(&pdev->dev, | ||
476 | p->irq[k].requested_irq, | ||
477 | intc_irqpin_irq_handler, | ||
478 | 0, name, &p->irq[k])) { | ||
479 | dev_err(&pdev->dev, | ||
480 | "failed to request low IRQ\n"); | ||
481 | ret = -ENOENT; | ||
482 | goto err1; | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | |||
487 | /* unmask all interrupts on prio level */ | ||
488 | for (k = 0; k < p->number_of_irqs; k++) | ||
489 | intc_irqpin_mask_unmask_prio(p, k, 0); | ||
490 | |||
491 | dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); | ||
492 | |||
493 | /* warn in case of mismatch if irq base is specified */ | ||
494 | if (p->config.irq_base) { | ||
495 | if (p->config.irq_base != p->irq[0].domain_irq) | ||
496 | dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", | ||
497 | p->config.irq_base, p->irq[0].domain_irq); | ||
498 | } | ||
499 | |||
500 | return 0; | ||
501 | |||
502 | err1: | ||
503 | irq_domain_remove(p->irq_domain); | ||
504 | err0: | ||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | static int intc_irqpin_remove(struct platform_device *pdev) | ||
509 | { | ||
510 | struct intc_irqpin_priv *p = platform_get_drvdata(pdev); | ||
511 | |||
512 | irq_domain_remove(p->irq_domain); | ||
513 | |||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static const struct of_device_id intc_irqpin_dt_ids[] = { | ||
518 | { .compatible = "renesas,intc-irqpin", }, | ||
519 | {}, | ||
520 | }; | ||
521 | MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); | ||
522 | |||
523 | static struct platform_driver intc_irqpin_device_driver = { | ||
524 | .probe = intc_irqpin_probe, | ||
525 | .remove = intc_irqpin_remove, | ||
526 | .driver = { | ||
527 | .name = "renesas_intc_irqpin", | ||
528 | .of_match_table = intc_irqpin_dt_ids, | ||
529 | .owner = THIS_MODULE, | ||
530 | } | ||
531 | }; | ||
532 | |||
533 | static int __init intc_irqpin_init(void) | ||
534 | { | ||
535 | return platform_driver_register(&intc_irqpin_device_driver); | ||
536 | } | ||
537 | postcore_initcall(intc_irqpin_init); | ||
538 | |||
539 | static void __exit intc_irqpin_exit(void) | ||
540 | { | ||
541 | platform_driver_unregister(&intc_irqpin_device_driver); | ||
542 | } | ||
543 | module_exit(intc_irqpin_exit); | ||
544 | |||
545 | MODULE_AUTHOR("Magnus Damm"); | ||
546 | MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); | ||
547 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 000000000000..927bff373aac --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /* | ||
2 | * Renesas IRQC Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Magnus Damm | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/irqdomain.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/platform_data/irq-renesas-irqc.h> | ||
32 | |||
33 | #define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ | ||
34 | |||
35 | #define IRQC_REQ_STS 0x00 | ||
36 | #define IRQC_EN_STS 0x04 | ||
37 | #define IRQC_EN_SET 0x08 | ||
38 | #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) | ||
39 | #define DETECT_STATUS 0x100 | ||
40 | #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) | ||
41 | |||
42 | struct irqc_irq { | ||
43 | int hw_irq; | ||
44 | int requested_irq; | ||
45 | int domain_irq; | ||
46 | struct irqc_priv *p; | ||
47 | }; | ||
48 | |||
49 | struct irqc_priv { | ||
50 | void __iomem *iomem; | ||
51 | void __iomem *cpu_int_base; | ||
52 | struct irqc_irq irq[IRQC_IRQ_MAX]; | ||
53 | struct renesas_irqc_config config; | ||
54 | unsigned int number_of_irqs; | ||
55 | struct platform_device *pdev; | ||
56 | struct irq_chip irq_chip; | ||
57 | struct irq_domain *irq_domain; | ||
58 | }; | ||
59 | |||
60 | static void irqc_dbg(struct irqc_irq *i, char *str) | ||
61 | { | ||
62 | dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", | ||
63 | str, i->requested_irq, i->hw_irq, i->domain_irq); | ||
64 | } | ||
65 | |||
66 | static void irqc_irq_enable(struct irq_data *d) | ||
67 | { | ||
68 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
69 | int hw_irq = irqd_to_hwirq(d); | ||
70 | |||
71 | irqc_dbg(&p->irq[hw_irq], "enable"); | ||
72 | iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); | ||
73 | } | ||
74 | |||
75 | static void irqc_irq_disable(struct irq_data *d) | ||
76 | { | ||
77 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
78 | int hw_irq = irqd_to_hwirq(d); | ||
79 | |||
80 | irqc_dbg(&p->irq[hw_irq], "disable"); | ||
81 | iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); | ||
82 | } | ||
83 | |||
84 | #define INTC_IRQ_SENSE_VALID 0x10 | ||
85 | #define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) | ||
86 | |||
87 | static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { | ||
88 | [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), | ||
89 | [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), | ||
90 | [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ | ||
91 | [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ | ||
92 | [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c), /* Synchronous */ | ||
93 | }; | ||
94 | |||
95 | static int irqc_irq_set_type(struct irq_data *d, unsigned int type) | ||
96 | { | ||
97 | struct irqc_priv *p = irq_data_get_irq_chip_data(d); | ||
98 | int hw_irq = irqd_to_hwirq(d); | ||
99 | unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; | ||
100 | unsigned long tmp; | ||
101 | |||
102 | irqc_dbg(&p->irq[hw_irq], "sense"); | ||
103 | |||
104 | if (!(value & INTC_IRQ_SENSE_VALID)) | ||
105 | return -EINVAL; | ||
106 | |||
107 | tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); | ||
108 | tmp &= ~0x3f; | ||
109 | tmp |= value ^ INTC_IRQ_SENSE_VALID; | ||
110 | iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static irqreturn_t irqc_irq_handler(int irq, void *dev_id) | ||
115 | { | ||
116 | struct irqc_irq *i = dev_id; | ||
117 | struct irqc_priv *p = i->p; | ||
118 | unsigned long bit = BIT(i->hw_irq); | ||
119 | |||
120 | irqc_dbg(i, "demux1"); | ||
121 | |||
122 | if (ioread32(p->iomem + DETECT_STATUS) & bit) { | ||
123 | iowrite32(bit, p->iomem + DETECT_STATUS); | ||
124 | irqc_dbg(i, "demux2"); | ||
125 | generic_handle_irq(i->domain_irq); | ||
126 | return IRQ_HANDLED; | ||
127 | } | ||
128 | return IRQ_NONE; | ||
129 | } | ||
130 | |||
131 | static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, | ||
132 | irq_hw_number_t hw) | ||
133 | { | ||
134 | struct irqc_priv *p = h->host_data; | ||
135 | |||
136 | p->irq[hw].domain_irq = virq; | ||
137 | p->irq[hw].hw_irq = hw; | ||
138 | |||
139 | irqc_dbg(&p->irq[hw], "map"); | ||
140 | irq_set_chip_data(virq, h->host_data); | ||
141 | irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); | ||
142 | set_irq_flags(virq, IRQF_VALID); /* kill me now */ | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct irq_domain_ops irqc_irq_domain_ops = { | ||
147 | .map = irqc_irq_domain_map, | ||
148 | .xlate = irq_domain_xlate_twocell, | ||
149 | }; | ||
150 | |||
151 | static int irqc_probe(struct platform_device *pdev) | ||
152 | { | ||
153 | struct renesas_irqc_config *pdata = pdev->dev.platform_data; | ||
154 | struct irqc_priv *p; | ||
155 | struct resource *io; | ||
156 | struct resource *irq; | ||
157 | struct irq_chip *irq_chip; | ||
158 | const char *name = dev_name(&pdev->dev); | ||
159 | int ret; | ||
160 | int k; | ||
161 | |||
162 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
163 | if (!p) { | ||
164 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | ||
165 | ret = -ENOMEM; | ||
166 | goto err0; | ||
167 | } | ||
168 | |||
169 | /* deal with driver instance configuration */ | ||
170 | if (pdata) | ||
171 | memcpy(&p->config, pdata, sizeof(*pdata)); | ||
172 | |||
173 | p->pdev = pdev; | ||
174 | platform_set_drvdata(pdev, p); | ||
175 | |||
176 | /* get hold of manadatory IOMEM */ | ||
177 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
178 | if (!io) { | ||
179 | dev_err(&pdev->dev, "not enough IOMEM resources\n"); | ||
180 | ret = -EINVAL; | ||
181 | goto err1; | ||
182 | } | ||
183 | |||
184 | /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ | ||
185 | for (k = 0; k < IRQC_IRQ_MAX; k++) { | ||
186 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); | ||
187 | if (!irq) | ||
188 | break; | ||
189 | |||
190 | p->irq[k].p = p; | ||
191 | p->irq[k].requested_irq = irq->start; | ||
192 | } | ||
193 | |||
194 | p->number_of_irqs = k; | ||
195 | if (p->number_of_irqs < 1) { | ||
196 | dev_err(&pdev->dev, "not enough IRQ resources\n"); | ||
197 | ret = -EINVAL; | ||
198 | goto err1; | ||
199 | } | ||
200 | |||
201 | /* ioremap IOMEM and setup read/write callbacks */ | ||
202 | p->iomem = ioremap_nocache(io->start, resource_size(io)); | ||
203 | if (!p->iomem) { | ||
204 | dev_err(&pdev->dev, "failed to remap IOMEM\n"); | ||
205 | ret = -ENXIO; | ||
206 | goto err2; | ||
207 | } | ||
208 | |||
209 | p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ | ||
210 | |||
211 | irq_chip = &p->irq_chip; | ||
212 | irq_chip->name = name; | ||
213 | irq_chip->irq_mask = irqc_irq_disable; | ||
214 | irq_chip->irq_unmask = irqc_irq_enable; | ||
215 | irq_chip->irq_enable = irqc_irq_enable; | ||
216 | irq_chip->irq_disable = irqc_irq_disable; | ||
217 | irq_chip->irq_set_type = irqc_irq_set_type; | ||
218 | irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; | ||
219 | |||
220 | p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, | ||
221 | p->number_of_irqs, | ||
222 | p->config.irq_base, | ||
223 | &irqc_irq_domain_ops, p); | ||
224 | if (!p->irq_domain) { | ||
225 | ret = -ENXIO; | ||
226 | dev_err(&pdev->dev, "cannot initialize irq domain\n"); | ||
227 | goto err2; | ||
228 | } | ||
229 | |||
230 | /* request interrupts one by one */ | ||
231 | for (k = 0; k < p->number_of_irqs; k++) { | ||
232 | if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, | ||
233 | 0, name, &p->irq[k])) { | ||
234 | dev_err(&pdev->dev, "failed to request IRQ\n"); | ||
235 | ret = -ENOENT; | ||
236 | goto err3; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); | ||
241 | |||
242 | /* warn in case of mismatch if irq base is specified */ | ||
243 | if (p->config.irq_base) { | ||
244 | if (p->config.irq_base != p->irq[0].domain_irq) | ||
245 | dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", | ||
246 | p->config.irq_base, p->irq[0].domain_irq); | ||
247 | } | ||
248 | |||
249 | return 0; | ||
250 | err3: | ||
251 | for (; k >= 0; k--) | ||
252 | free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); | ||
253 | |||
254 | irq_domain_remove(p->irq_domain); | ||
255 | err2: | ||
256 | iounmap(p->iomem); | ||
257 | err1: | ||
258 | kfree(p); | ||
259 | err0: | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static int irqc_remove(struct platform_device *pdev) | ||
264 | { | ||
265 | struct irqc_priv *p = platform_get_drvdata(pdev); | ||
266 | int k; | ||
267 | |||
268 | for (k = 0; k < p->number_of_irqs; k++) | ||
269 | free_irq(p->irq[k].requested_irq, &p->irq[k]); | ||
270 | |||
271 | irq_domain_remove(p->irq_domain); | ||
272 | iounmap(p->iomem); | ||
273 | kfree(p); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static const struct of_device_id irqc_dt_ids[] = { | ||
278 | { .compatible = "renesas,irqc", }, | ||
279 | {}, | ||
280 | }; | ||
281 | MODULE_DEVICE_TABLE(of, irqc_dt_ids); | ||
282 | |||
283 | static struct platform_driver irqc_device_driver = { | ||
284 | .probe = irqc_probe, | ||
285 | .remove = irqc_remove, | ||
286 | .driver = { | ||
287 | .name = "renesas_irqc", | ||
288 | .of_match_table = irqc_dt_ids, | ||
289 | .owner = THIS_MODULE, | ||
290 | } | ||
291 | }; | ||
292 | |||
293 | static int __init irqc_init(void) | ||
294 | { | ||
295 | return platform_driver_register(&irqc_device_driver); | ||
296 | } | ||
297 | postcore_initcall(irqc_init); | ||
298 | |||
299 | static void __exit irqc_exit(void) | ||
300 | { | ||
301 | platform_driver_unregister(&irqc_device_driver); | ||
302 | } | ||
303 | module_exit(irqc_exit); | ||
304 | |||
305 | MODULE_AUTHOR("Magnus Damm"); | ||
306 | MODULE_DESCRIPTION("Renesas IRQC Driver"); | ||
307 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c new file mode 100644 index 000000000000..bbcc944ed94f --- /dev/null +++ b/drivers/irqchip/irq-s3c24xx.c | |||
@@ -0,0 +1,1356 @@ | |||
1 | /* | ||
2 | * S3C24XX IRQ handling | ||
3 | * | ||
4 | * Copyright (c) 2003-2004 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * Copyright (c) 2012 Heiko Stuebner <heiko@sntech.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/ioport.h> | ||
26 | #include <linux/device.h> | ||
27 | #include <linux/irqdomain.h> | ||
28 | #include <linux/irqchip/chained_irq.h> | ||
29 | #include <linux/of.h> | ||
30 | #include <linux/of_irq.h> | ||
31 | #include <linux/of_address.h> | ||
32 | |||
33 | #include <asm/exception.h> | ||
34 | #include <asm/mach/irq.h> | ||
35 | |||
36 | #include <mach/regs-irq.h> | ||
37 | #include <mach/regs-gpio.h> | ||
38 | |||
39 | #include <plat/cpu.h> | ||
40 | #include <plat/regs-irqtype.h> | ||
41 | #include <plat/pm.h> | ||
42 | |||
43 | #include "irqchip.h" | ||
44 | |||
45 | #define S3C_IRQTYPE_NONE 0 | ||
46 | #define S3C_IRQTYPE_EINT 1 | ||
47 | #define S3C_IRQTYPE_EDGE 2 | ||
48 | #define S3C_IRQTYPE_LEVEL 3 | ||
49 | |||
50 | struct s3c_irq_data { | ||
51 | unsigned int type; | ||
52 | unsigned long offset; | ||
53 | unsigned long parent_irq; | ||
54 | |||
55 | /* data gets filled during init */ | ||
56 | struct s3c_irq_intc *intc; | ||
57 | unsigned long sub_bits; | ||
58 | struct s3c_irq_intc *sub_intc; | ||
59 | }; | ||
60 | |||
61 | /* | ||
62 | * Sructure holding the controller data | ||
63 | * @reg_pending register holding pending irqs | ||
64 | * @reg_intpnd special register intpnd in main intc | ||
65 | * @reg_mask mask register | ||
66 | * @domain irq_domain of the controller | ||
67 | * @parent parent controller for ext and sub irqs | ||
68 | * @irqs irq-data, always s3c_irq_data[32] | ||
69 | */ | ||
70 | struct s3c_irq_intc { | ||
71 | void __iomem *reg_pending; | ||
72 | void __iomem *reg_intpnd; | ||
73 | void __iomem *reg_mask; | ||
74 | struct irq_domain *domain; | ||
75 | struct s3c_irq_intc *parent; | ||
76 | struct s3c_irq_data *irqs; | ||
77 | }; | ||
78 | |||
79 | /* | ||
80 | * Array holding pointers to the global controller structs | ||
81 | * [0] ... main_intc | ||
82 | * [1] ... sub_intc | ||
83 | * [2] ... main_intc2 on s3c2416 | ||
84 | */ | ||
85 | static struct s3c_irq_intc *s3c_intc[3]; | ||
86 | |||
87 | static void s3c_irq_mask(struct irq_data *data) | ||
88 | { | ||
89 | struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); | ||
90 | struct s3c_irq_intc *intc = irq_data->intc; | ||
91 | struct s3c_irq_intc *parent_intc = intc->parent; | ||
92 | struct s3c_irq_data *parent_data; | ||
93 | unsigned long mask; | ||
94 | unsigned int irqno; | ||
95 | |||
96 | mask = __raw_readl(intc->reg_mask); | ||
97 | mask |= (1UL << irq_data->offset); | ||
98 | __raw_writel(mask, intc->reg_mask); | ||
99 | |||
100 | if (parent_intc) { | ||
101 | parent_data = &parent_intc->irqs[irq_data->parent_irq]; | ||
102 | |||
103 | /* check to see if we need to mask the parent IRQ | ||
104 | * The parent_irq is always in main_intc, so the hwirq | ||
105 | * for find_mapping does not need an offset in any case. | ||
106 | */ | ||
107 | if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { | ||
108 | irqno = irq_find_mapping(parent_intc->domain, | ||
109 | irq_data->parent_irq); | ||
110 | s3c_irq_mask(irq_get_irq_data(irqno)); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | static void s3c_irq_unmask(struct irq_data *data) | ||
116 | { | ||
117 | struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); | ||
118 | struct s3c_irq_intc *intc = irq_data->intc; | ||
119 | struct s3c_irq_intc *parent_intc = intc->parent; | ||
120 | unsigned long mask; | ||
121 | unsigned int irqno; | ||
122 | |||
123 | mask = __raw_readl(intc->reg_mask); | ||
124 | mask &= ~(1UL << irq_data->offset); | ||
125 | __raw_writel(mask, intc->reg_mask); | ||
126 | |||
127 | if (parent_intc) { | ||
128 | irqno = irq_find_mapping(parent_intc->domain, | ||
129 | irq_data->parent_irq); | ||
130 | s3c_irq_unmask(irq_get_irq_data(irqno)); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static inline void s3c_irq_ack(struct irq_data *data) | ||
135 | { | ||
136 | struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); | ||
137 | struct s3c_irq_intc *intc = irq_data->intc; | ||
138 | unsigned long bitval = 1UL << irq_data->offset; | ||
139 | |||
140 | __raw_writel(bitval, intc->reg_pending); | ||
141 | if (intc->reg_intpnd) | ||
142 | __raw_writel(bitval, intc->reg_intpnd); | ||
143 | } | ||
144 | |||
145 | static int s3c_irq_type(struct irq_data *data, unsigned int type) | ||
146 | { | ||
147 | switch (type) { | ||
148 | case IRQ_TYPE_NONE: | ||
149 | break; | ||
150 | case IRQ_TYPE_EDGE_RISING: | ||
151 | case IRQ_TYPE_EDGE_FALLING: | ||
152 | case IRQ_TYPE_EDGE_BOTH: | ||
153 | irq_set_handler(data->irq, handle_edge_irq); | ||
154 | break; | ||
155 | case IRQ_TYPE_LEVEL_LOW: | ||
156 | case IRQ_TYPE_LEVEL_HIGH: | ||
157 | irq_set_handler(data->irq, handle_level_irq); | ||
158 | break; | ||
159 | default: | ||
160 | pr_err("No such irq type %d", type); | ||
161 | return -EINVAL; | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int s3c_irqext_type_set(void __iomem *gpcon_reg, | ||
168 | void __iomem *extint_reg, | ||
169 | unsigned long gpcon_offset, | ||
170 | unsigned long extint_offset, | ||
171 | unsigned int type) | ||
172 | { | ||
173 | unsigned long newvalue = 0, value; | ||
174 | |||
175 | /* Set the GPIO to external interrupt mode */ | ||
176 | value = __raw_readl(gpcon_reg); | ||
177 | value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); | ||
178 | __raw_writel(value, gpcon_reg); | ||
179 | |||
180 | /* Set the external interrupt to pointed trigger type */ | ||
181 | switch (type) | ||
182 | { | ||
183 | case IRQ_TYPE_NONE: | ||
184 | pr_warn("No edge setting!\n"); | ||
185 | break; | ||
186 | |||
187 | case IRQ_TYPE_EDGE_RISING: | ||
188 | newvalue = S3C2410_EXTINT_RISEEDGE; | ||
189 | break; | ||
190 | |||
191 | case IRQ_TYPE_EDGE_FALLING: | ||
192 | newvalue = S3C2410_EXTINT_FALLEDGE; | ||
193 | break; | ||
194 | |||
195 | case IRQ_TYPE_EDGE_BOTH: | ||
196 | newvalue = S3C2410_EXTINT_BOTHEDGE; | ||
197 | break; | ||
198 | |||
199 | case IRQ_TYPE_LEVEL_LOW: | ||
200 | newvalue = S3C2410_EXTINT_LOWLEV; | ||
201 | break; | ||
202 | |||
203 | case IRQ_TYPE_LEVEL_HIGH: | ||
204 | newvalue = S3C2410_EXTINT_HILEV; | ||
205 | break; | ||
206 | |||
207 | default: | ||
208 | pr_err("No such irq type %d", type); | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | |||
212 | value = __raw_readl(extint_reg); | ||
213 | value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); | ||
214 | __raw_writel(value, extint_reg); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int s3c_irqext_type(struct irq_data *data, unsigned int type) | ||
220 | { | ||
221 | void __iomem *extint_reg; | ||
222 | void __iomem *gpcon_reg; | ||
223 | unsigned long gpcon_offset, extint_offset; | ||
224 | |||
225 | if ((data->hwirq >= 4) && (data->hwirq <= 7)) { | ||
226 | gpcon_reg = S3C2410_GPFCON; | ||
227 | extint_reg = S3C24XX_EXTINT0; | ||
228 | gpcon_offset = (data->hwirq) * 2; | ||
229 | extint_offset = (data->hwirq) * 4; | ||
230 | } else if ((data->hwirq >= 8) && (data->hwirq <= 15)) { | ||
231 | gpcon_reg = S3C2410_GPGCON; | ||
232 | extint_reg = S3C24XX_EXTINT1; | ||
233 | gpcon_offset = (data->hwirq - 8) * 2; | ||
234 | extint_offset = (data->hwirq - 8) * 4; | ||
235 | } else if ((data->hwirq >= 16) && (data->hwirq <= 23)) { | ||
236 | gpcon_reg = S3C2410_GPGCON; | ||
237 | extint_reg = S3C24XX_EXTINT2; | ||
238 | gpcon_offset = (data->hwirq - 8) * 2; | ||
239 | extint_offset = (data->hwirq - 16) * 4; | ||
240 | } else { | ||
241 | return -EINVAL; | ||
242 | } | ||
243 | |||
244 | return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, | ||
245 | extint_offset, type); | ||
246 | } | ||
247 | |||
248 | static int s3c_irqext0_type(struct irq_data *data, unsigned int type) | ||
249 | { | ||
250 | void __iomem *extint_reg; | ||
251 | void __iomem *gpcon_reg; | ||
252 | unsigned long gpcon_offset, extint_offset; | ||
253 | |||
254 | if ((data->hwirq >= 0) && (data->hwirq <= 3)) { | ||
255 | gpcon_reg = S3C2410_GPFCON; | ||
256 | extint_reg = S3C24XX_EXTINT0; | ||
257 | gpcon_offset = (data->hwirq) * 2; | ||
258 | extint_offset = (data->hwirq) * 4; | ||
259 | } else { | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | |||
263 | return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, | ||
264 | extint_offset, type); | ||
265 | } | ||
266 | |||
267 | static struct irq_chip s3c_irq_chip = { | ||
268 | .name = "s3c", | ||
269 | .irq_ack = s3c_irq_ack, | ||
270 | .irq_mask = s3c_irq_mask, | ||
271 | .irq_unmask = s3c_irq_unmask, | ||
272 | .irq_set_type = s3c_irq_type, | ||
273 | .irq_set_wake = s3c_irq_wake | ||
274 | }; | ||
275 | |||
276 | static struct irq_chip s3c_irq_level_chip = { | ||
277 | .name = "s3c-level", | ||
278 | .irq_mask = s3c_irq_mask, | ||
279 | .irq_unmask = s3c_irq_unmask, | ||
280 | .irq_ack = s3c_irq_ack, | ||
281 | .irq_set_type = s3c_irq_type, | ||
282 | }; | ||
283 | |||
284 | static struct irq_chip s3c_irqext_chip = { | ||
285 | .name = "s3c-ext", | ||
286 | .irq_mask = s3c_irq_mask, | ||
287 | .irq_unmask = s3c_irq_unmask, | ||
288 | .irq_ack = s3c_irq_ack, | ||
289 | .irq_set_type = s3c_irqext_type, | ||
290 | .irq_set_wake = s3c_irqext_wake | ||
291 | }; | ||
292 | |||
293 | static struct irq_chip s3c_irq_eint0t4 = { | ||
294 | .name = "s3c-ext0", | ||
295 | .irq_ack = s3c_irq_ack, | ||
296 | .irq_mask = s3c_irq_mask, | ||
297 | .irq_unmask = s3c_irq_unmask, | ||
298 | .irq_set_wake = s3c_irq_wake, | ||
299 | .irq_set_type = s3c_irqext0_type, | ||
300 | }; | ||
301 | |||
302 | static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) | ||
303 | { | ||
304 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
305 | struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); | ||
306 | struct s3c_irq_intc *intc = irq_data->intc; | ||
307 | struct s3c_irq_intc *sub_intc = irq_data->sub_intc; | ||
308 | unsigned long src; | ||
309 | unsigned long msk; | ||
310 | unsigned int n; | ||
311 | unsigned int offset; | ||
312 | |||
313 | /* we're using individual domains for the non-dt case | ||
314 | * and one big domain for the dt case where the subintc | ||
315 | * starts at hwirq number 32. | ||
316 | */ | ||
317 | offset = (intc->domain->of_node) ? 32 : 0; | ||
318 | |||
319 | chained_irq_enter(chip, desc); | ||
320 | |||
321 | src = __raw_readl(sub_intc->reg_pending); | ||
322 | msk = __raw_readl(sub_intc->reg_mask); | ||
323 | |||
324 | src &= ~msk; | ||
325 | src &= irq_data->sub_bits; | ||
326 | |||
327 | while (src) { | ||
328 | n = __ffs(src); | ||
329 | src &= ~(1 << n); | ||
330 | irq = irq_find_mapping(sub_intc->domain, offset + n); | ||
331 | generic_handle_irq(irq); | ||
332 | } | ||
333 | |||
334 | chained_irq_exit(chip, desc); | ||
335 | } | ||
336 | |||
337 | static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, | ||
338 | struct pt_regs *regs, int intc_offset) | ||
339 | { | ||
340 | int pnd; | ||
341 | int offset; | ||
342 | int irq; | ||
343 | |||
344 | pnd = __raw_readl(intc->reg_intpnd); | ||
345 | if (!pnd) | ||
346 | return false; | ||
347 | |||
348 | /* non-dt machines use individual domains */ | ||
349 | if (!intc->domain->of_node) | ||
350 | intc_offset = 0; | ||
351 | |||
352 | /* We have a problem that the INTOFFSET register does not always | ||
353 | * show one interrupt. Occasionally we get two interrupts through | ||
354 | * the prioritiser, and this causes the INTOFFSET register to show | ||
355 | * what looks like the logical-or of the two interrupt numbers. | ||
356 | * | ||
357 | * Thanks to Klaus, Shannon, et al for helping to debug this problem | ||
358 | */ | ||
359 | offset = __raw_readl(intc->reg_intpnd + 4); | ||
360 | |||
361 | /* Find the bit manually, when the offset is wrong. | ||
362 | * The pending register only ever contains the one bit of the next | ||
363 | * interrupt to handle. | ||
364 | */ | ||
365 | if (!(pnd & (1 << offset))) | ||
366 | offset = __ffs(pnd); | ||
367 | |||
368 | irq = irq_find_mapping(intc->domain, intc_offset + offset); | ||
369 | handle_IRQ(irq, regs); | ||
370 | return true; | ||
371 | } | ||
372 | |||
373 | asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) | ||
374 | { | ||
375 | do { | ||
376 | if (likely(s3c_intc[0])) | ||
377 | if (s3c24xx_handle_intc(s3c_intc[0], regs, 0)) | ||
378 | continue; | ||
379 | |||
380 | if (s3c_intc[2]) | ||
381 | if (s3c24xx_handle_intc(s3c_intc[2], regs, 64)) | ||
382 | continue; | ||
383 | |||
384 | break; | ||
385 | } while (1); | ||
386 | } | ||
387 | |||
388 | #ifdef CONFIG_FIQ | ||
389 | /** | ||
390 | * s3c24xx_set_fiq - set the FIQ routing | ||
391 | * @irq: IRQ number to route to FIQ on processor. | ||
392 | * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing. | ||
393 | * | ||
394 | * Change the state of the IRQ to FIQ routing depending on @irq and @on. If | ||
395 | * @on is true, the @irq is checked to see if it can be routed and the | ||
396 | * interrupt controller updated to route the IRQ. If @on is false, the FIQ | ||
397 | * routing is cleared, regardless of which @irq is specified. | ||
398 | */ | ||
399 | int s3c24xx_set_fiq(unsigned int irq, bool on) | ||
400 | { | ||
401 | u32 intmod; | ||
402 | unsigned offs; | ||
403 | |||
404 | if (on) { | ||
405 | offs = irq - FIQ_START; | ||
406 | if (offs > 31) | ||
407 | return -EINVAL; | ||
408 | |||
409 | intmod = 1 << offs; | ||
410 | } else { | ||
411 | intmod = 0; | ||
412 | } | ||
413 | |||
414 | __raw_writel(intmod, S3C2410_INTMOD); | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | EXPORT_SYMBOL_GPL(s3c24xx_set_fiq); | ||
419 | #endif | ||
420 | |||
421 | static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, | ||
422 | irq_hw_number_t hw) | ||
423 | { | ||
424 | struct s3c_irq_intc *intc = h->host_data; | ||
425 | struct s3c_irq_data *irq_data = &intc->irqs[hw]; | ||
426 | struct s3c_irq_intc *parent_intc; | ||
427 | struct s3c_irq_data *parent_irq_data; | ||
428 | unsigned int irqno; | ||
429 | |||
430 | /* attach controller pointer to irq_data */ | ||
431 | irq_data->intc = intc; | ||
432 | irq_data->offset = hw; | ||
433 | |||
434 | parent_intc = intc->parent; | ||
435 | |||
436 | /* set handler and flags */ | ||
437 | switch (irq_data->type) { | ||
438 | case S3C_IRQTYPE_NONE: | ||
439 | return 0; | ||
440 | case S3C_IRQTYPE_EINT: | ||
441 | /* On the S3C2412, the EINT0to3 have a parent irq | ||
442 | * but need the s3c_irq_eint0t4 chip | ||
443 | */ | ||
444 | if (parent_intc && (!soc_is_s3c2412() || hw >= 4)) | ||
445 | irq_set_chip_and_handler(virq, &s3c_irqext_chip, | ||
446 | handle_edge_irq); | ||
447 | else | ||
448 | irq_set_chip_and_handler(virq, &s3c_irq_eint0t4, | ||
449 | handle_edge_irq); | ||
450 | break; | ||
451 | case S3C_IRQTYPE_EDGE: | ||
452 | if (parent_intc || intc->reg_pending == S3C2416_SRCPND2) | ||
453 | irq_set_chip_and_handler(virq, &s3c_irq_level_chip, | ||
454 | handle_edge_irq); | ||
455 | else | ||
456 | irq_set_chip_and_handler(virq, &s3c_irq_chip, | ||
457 | handle_edge_irq); | ||
458 | break; | ||
459 | case S3C_IRQTYPE_LEVEL: | ||
460 | if (parent_intc) | ||
461 | irq_set_chip_and_handler(virq, &s3c_irq_level_chip, | ||
462 | handle_level_irq); | ||
463 | else | ||
464 | irq_set_chip_and_handler(virq, &s3c_irq_chip, | ||
465 | handle_level_irq); | ||
466 | break; | ||
467 | default: | ||
468 | pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type); | ||
469 | return -EINVAL; | ||
470 | } | ||
471 | |||
472 | irq_set_chip_data(virq, irq_data); | ||
473 | |||
474 | set_irq_flags(virq, IRQF_VALID); | ||
475 | |||
476 | if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { | ||
477 | if (irq_data->parent_irq > 31) { | ||
478 | pr_err("irq-s3c24xx: parent irq %lu is out of range\n", | ||
479 | irq_data->parent_irq); | ||
480 | goto err; | ||
481 | } | ||
482 | |||
483 | parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; | ||
484 | parent_irq_data->sub_intc = intc; | ||
485 | parent_irq_data->sub_bits |= (1UL << hw); | ||
486 | |||
487 | /* attach the demuxer to the parent irq */ | ||
488 | irqno = irq_find_mapping(parent_intc->domain, | ||
489 | irq_data->parent_irq); | ||
490 | if (!irqno) { | ||
491 | pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n", | ||
492 | irq_data->parent_irq); | ||
493 | goto err; | ||
494 | } | ||
495 | irq_set_chained_handler(irqno, s3c_irq_demux); | ||
496 | } | ||
497 | |||
498 | return 0; | ||
499 | |||
500 | err: | ||
501 | set_irq_flags(virq, 0); | ||
502 | |||
503 | /* the only error can result from bad mapping data*/ | ||
504 | return -EINVAL; | ||
505 | } | ||
506 | |||
507 | static struct irq_domain_ops s3c24xx_irq_ops = { | ||
508 | .map = s3c24xx_irq_map, | ||
509 | .xlate = irq_domain_xlate_twocell, | ||
510 | }; | ||
511 | |||
512 | static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) | ||
513 | { | ||
514 | void __iomem *reg_source; | ||
515 | unsigned long pend; | ||
516 | unsigned long last; | ||
517 | int i; | ||
518 | |||
519 | /* if intpnd is set, read the next pending irq from there */ | ||
520 | reg_source = intc->reg_intpnd ? intc->reg_intpnd : intc->reg_pending; | ||
521 | |||
522 | last = 0; | ||
523 | for (i = 0; i < 4; i++) { | ||
524 | pend = __raw_readl(reg_source); | ||
525 | |||
526 | if (pend == 0 || pend == last) | ||
527 | break; | ||
528 | |||
529 | __raw_writel(pend, intc->reg_pending); | ||
530 | if (intc->reg_intpnd) | ||
531 | __raw_writel(pend, intc->reg_intpnd); | ||
532 | |||
533 | pr_info("irq: clearing pending status %08x\n", (int)pend); | ||
534 | last = pend; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | static struct s3c_irq_intc * __init s3c24xx_init_intc(struct device_node *np, | ||
539 | struct s3c_irq_data *irq_data, | ||
540 | struct s3c_irq_intc *parent, | ||
541 | unsigned long address) | ||
542 | { | ||
543 | struct s3c_irq_intc *intc; | ||
544 | void __iomem *base = (void *)0xf6000000; /* static mapping */ | ||
545 | int irq_num; | ||
546 | int irq_start; | ||
547 | int ret; | ||
548 | |||
549 | intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); | ||
550 | if (!intc) | ||
551 | return ERR_PTR(-ENOMEM); | ||
552 | |||
553 | intc->irqs = irq_data; | ||
554 | |||
555 | if (parent) | ||
556 | intc->parent = parent; | ||
557 | |||
558 | /* select the correct data for the controller. | ||
559 | * Need to hard code the irq num start and offset | ||
560 | * to preserve the static mapping for now | ||
561 | */ | ||
562 | switch (address) { | ||
563 | case 0x4a000000: | ||
564 | pr_debug("irq: found main intc\n"); | ||
565 | intc->reg_pending = base; | ||
566 | intc->reg_mask = base + 0x08; | ||
567 | intc->reg_intpnd = base + 0x10; | ||
568 | irq_num = 32; | ||
569 | irq_start = S3C2410_IRQ(0); | ||
570 | break; | ||
571 | case 0x4a000018: | ||
572 | pr_debug("irq: found subintc\n"); | ||
573 | intc->reg_pending = base + 0x18; | ||
574 | intc->reg_mask = base + 0x1c; | ||
575 | irq_num = 29; | ||
576 | irq_start = S3C2410_IRQSUB(0); | ||
577 | break; | ||
578 | case 0x4a000040: | ||
579 | pr_debug("irq: found intc2\n"); | ||
580 | intc->reg_pending = base + 0x40; | ||
581 | intc->reg_mask = base + 0x48; | ||
582 | intc->reg_intpnd = base + 0x50; | ||
583 | irq_num = 8; | ||
584 | irq_start = S3C2416_IRQ(0); | ||
585 | break; | ||
586 | case 0x560000a4: | ||
587 | pr_debug("irq: found eintc\n"); | ||
588 | base = (void *)0xfd000000; | ||
589 | |||
590 | intc->reg_mask = base + 0xa4; | ||
591 | intc->reg_pending = base + 0xa8; | ||
592 | irq_num = 24; | ||
593 | irq_start = S3C2410_IRQ(32); | ||
594 | break; | ||
595 | default: | ||
596 | pr_err("irq: unsupported controller address\n"); | ||
597 | ret = -EINVAL; | ||
598 | goto err; | ||
599 | } | ||
600 | |||
601 | /* now that all the data is complete, init the irq-domain */ | ||
602 | s3c24xx_clear_intc(intc); | ||
603 | intc->domain = irq_domain_add_legacy(np, irq_num, irq_start, | ||
604 | 0, &s3c24xx_irq_ops, | ||
605 | intc); | ||
606 | if (!intc->domain) { | ||
607 | pr_err("irq: could not create irq-domain\n"); | ||
608 | ret = -EINVAL; | ||
609 | goto err; | ||
610 | } | ||
611 | |||
612 | set_handle_irq(s3c24xx_handle_irq); | ||
613 | |||
614 | return intc; | ||
615 | |||
616 | err: | ||
617 | kfree(intc); | ||
618 | return ERR_PTR(ret); | ||
619 | } | ||
620 | |||
621 | static struct s3c_irq_data init_eint[32] = { | ||
622 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
623 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
624 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
625 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
626 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ | ||
627 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ | ||
628 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ | ||
629 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ | ||
630 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ | ||
631 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ | ||
632 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ | ||
633 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ | ||
634 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ | ||
635 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ | ||
636 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ | ||
637 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ | ||
638 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ | ||
639 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ | ||
640 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ | ||
641 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ | ||
642 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ | ||
643 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ | ||
644 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ | ||
645 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ | ||
646 | }; | ||
647 | |||
648 | #ifdef CONFIG_CPU_S3C2410 | ||
649 | static struct s3c_irq_data init_s3c2410base[32] = { | ||
650 | { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ | ||
651 | { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ | ||
652 | { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ | ||
653 | { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ | ||
654 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
655 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
656 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
657 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
658 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
659 | { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ | ||
660 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
661 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
662 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
663 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
664 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
665 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
666 | { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ | ||
667 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ | ||
668 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ | ||
669 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ | ||
670 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ | ||
671 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ | ||
672 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
673 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
674 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
675 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
676 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
677 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
678 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
679 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ | ||
680 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
681 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
682 | }; | ||
683 | |||
684 | static struct s3c_irq_data init_s3c2410subint[32] = { | ||
685 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
686 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
687 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
688 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
689 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
690 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
691 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
692 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
693 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
694 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
695 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
696 | }; | ||
697 | |||
698 | void __init s3c2410_init_irq(void) | ||
699 | { | ||
700 | #ifdef CONFIG_FIQ | ||
701 | init_FIQ(FIQ_START); | ||
702 | #endif | ||
703 | |||
704 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, | ||
705 | 0x4a000000); | ||
706 | if (IS_ERR(s3c_intc[0])) { | ||
707 | pr_err("irq: could not create main interrupt controller\n"); | ||
708 | return; | ||
709 | } | ||
710 | |||
711 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0], | ||
712 | s3c_intc[0], 0x4a000018); | ||
713 | s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); | ||
714 | } | ||
715 | #endif | ||
716 | |||
717 | #ifdef CONFIG_CPU_S3C2412 | ||
718 | static struct s3c_irq_data init_s3c2412base[32] = { | ||
719 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT0 */ | ||
720 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT1 */ | ||
721 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT2 */ | ||
722 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT3 */ | ||
723 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
724 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
725 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
726 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
727 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
728 | { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ | ||
729 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
730 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
731 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
732 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
733 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
734 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
735 | { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ | ||
736 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ | ||
737 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ | ||
738 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ | ||
739 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ | ||
740 | { .type = S3C_IRQTYPE_LEVEL, }, /* SDI/CF */ | ||
741 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
742 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
743 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
744 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
745 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
746 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
747 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
748 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ | ||
749 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
750 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
751 | }; | ||
752 | |||
753 | static struct s3c_irq_data init_s3c2412eint[32] = { | ||
754 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 0 }, /* EINT0 */ | ||
755 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 1 }, /* EINT1 */ | ||
756 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 2 }, /* EINT2 */ | ||
757 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 3 }, /* EINT3 */ | ||
758 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ | ||
759 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ | ||
760 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ | ||
761 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ | ||
762 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ | ||
763 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ | ||
764 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ | ||
765 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ | ||
766 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ | ||
767 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ | ||
768 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ | ||
769 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ | ||
770 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ | ||
771 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ | ||
772 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ | ||
773 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ | ||
774 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ | ||
775 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ | ||
776 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ | ||
777 | { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ | ||
778 | }; | ||
779 | |||
780 | static struct s3c_irq_data init_s3c2412subint[32] = { | ||
781 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
782 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
783 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
784 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
785 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
786 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
787 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
788 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
789 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
790 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
791 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
792 | { .type = S3C_IRQTYPE_NONE, }, | ||
793 | { .type = S3C_IRQTYPE_NONE, }, | ||
794 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* SDI */ | ||
795 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */ | ||
796 | }; | ||
797 | |||
798 | void __init s3c2412_init_irq(void) | ||
799 | { | ||
800 | pr_info("S3C2412: IRQ Support\n"); | ||
801 | |||
802 | #ifdef CONFIG_FIQ | ||
803 | init_FIQ(FIQ_START); | ||
804 | #endif | ||
805 | |||
806 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, | ||
807 | 0x4a000000); | ||
808 | if (IS_ERR(s3c_intc[0])) { | ||
809 | pr_err("irq: could not create main interrupt controller\n"); | ||
810 | return; | ||
811 | } | ||
812 | |||
813 | s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4); | ||
814 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0], | ||
815 | s3c_intc[0], 0x4a000018); | ||
816 | } | ||
817 | #endif | ||
818 | |||
819 | #ifdef CONFIG_CPU_S3C2416 | ||
820 | static struct s3c_irq_data init_s3c2416base[32] = { | ||
821 | { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ | ||
822 | { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ | ||
823 | { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ | ||
824 | { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ | ||
825 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
826 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
827 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
828 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
829 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
830 | { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ | ||
831 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
832 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
833 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
834 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
835 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
836 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
837 | { .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ | ||
838 | { .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ | ||
839 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ | ||
840 | { .type = S3C_IRQTYPE_NONE, }, /* reserved */ | ||
841 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ | ||
842 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ | ||
843 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
844 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
845 | { .type = S3C_IRQTYPE_EDGE, }, /* NAND */ | ||
846 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
847 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
848 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
849 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
850 | { .type = S3C_IRQTYPE_NONE, }, | ||
851 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
852 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
853 | }; | ||
854 | |||
855 | static struct s3c_irq_data init_s3c2416subint[32] = { | ||
856 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
857 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
858 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
859 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
860 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
861 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
862 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
863 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
864 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
865 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
866 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
867 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
868 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
869 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
870 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
871 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ | ||
872 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ | ||
873 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ | ||
874 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ | ||
875 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ | ||
876 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ | ||
877 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ | ||
878 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ | ||
879 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ | ||
880 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ | ||
881 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ | ||
882 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ | ||
883 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ | ||
884 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ | ||
885 | }; | ||
886 | |||
887 | static struct s3c_irq_data init_s3c2416_second[32] = { | ||
888 | { .type = S3C_IRQTYPE_EDGE }, /* 2D */ | ||
889 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
890 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
891 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
892 | { .type = S3C_IRQTYPE_EDGE }, /* PCM0 */ | ||
893 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
894 | { .type = S3C_IRQTYPE_EDGE }, /* I2S0 */ | ||
895 | }; | ||
896 | |||
897 | void __init s3c2416_init_irq(void) | ||
898 | { | ||
899 | pr_info("S3C2416: IRQ Support\n"); | ||
900 | |||
901 | #ifdef CONFIG_FIQ | ||
902 | init_FIQ(FIQ_START); | ||
903 | #endif | ||
904 | |||
905 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, | ||
906 | 0x4a000000); | ||
907 | if (IS_ERR(s3c_intc[0])) { | ||
908 | pr_err("irq: could not create main interrupt controller\n"); | ||
909 | return; | ||
910 | } | ||
911 | |||
912 | s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); | ||
913 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0], | ||
914 | s3c_intc[0], 0x4a000018); | ||
915 | |||
916 | s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0], | ||
917 | NULL, 0x4a000040); | ||
918 | } | ||
919 | |||
920 | #endif | ||
921 | |||
922 | #ifdef CONFIG_CPU_S3C2440 | ||
923 | static struct s3c_irq_data init_s3c2440base[32] = { | ||
924 | { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ | ||
925 | { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ | ||
926 | { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ | ||
927 | { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ | ||
928 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
929 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
930 | { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ | ||
931 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
932 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
933 | { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ | ||
934 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
935 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
936 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
937 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
938 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
939 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
940 | { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ | ||
941 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ | ||
942 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ | ||
943 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ | ||
944 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ | ||
945 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ | ||
946 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
947 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
948 | { .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ | ||
949 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
950 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
951 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
952 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
953 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ | ||
954 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
955 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
956 | }; | ||
957 | |||
958 | static struct s3c_irq_data init_s3c2440subint[32] = { | ||
959 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
960 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
961 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
962 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
963 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
964 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
965 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
966 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
967 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
968 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
969 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
970 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ | ||
971 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ | ||
972 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ | ||
973 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ | ||
974 | }; | ||
975 | |||
976 | void __init s3c2440_init_irq(void) | ||
977 | { | ||
978 | pr_info("S3C2440: IRQ Support\n"); | ||
979 | |||
980 | #ifdef CONFIG_FIQ | ||
981 | init_FIQ(FIQ_START); | ||
982 | #endif | ||
983 | |||
984 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, | ||
985 | 0x4a000000); | ||
986 | if (IS_ERR(s3c_intc[0])) { | ||
987 | pr_err("irq: could not create main interrupt controller\n"); | ||
988 | return; | ||
989 | } | ||
990 | |||
991 | s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); | ||
992 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0], | ||
993 | s3c_intc[0], 0x4a000018); | ||
994 | } | ||
995 | #endif | ||
996 | |||
997 | #ifdef CONFIG_CPU_S3C2442 | ||
998 | static struct s3c_irq_data init_s3c2442base[32] = { | ||
999 | { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ | ||
1000 | { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ | ||
1001 | { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ | ||
1002 | { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ | ||
1003 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
1004 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
1005 | { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ | ||
1006 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
1007 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
1008 | { .type = S3C_IRQTYPE_EDGE, }, /* WDT */ | ||
1009 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
1010 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
1011 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
1012 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
1013 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
1014 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
1015 | { .type = S3C_IRQTYPE_EDGE, }, /* LCD */ | ||
1016 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ | ||
1017 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ | ||
1018 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ | ||
1019 | { .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ | ||
1020 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI */ | ||
1021 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
1022 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
1023 | { .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ | ||
1024 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
1025 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
1026 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
1027 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
1028 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ | ||
1029 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
1030 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
1031 | }; | ||
1032 | |||
1033 | static struct s3c_irq_data init_s3c2442subint[32] = { | ||
1034 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
1035 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
1036 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
1037 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
1038 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
1039 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
1040 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
1041 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
1042 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
1043 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
1044 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
1045 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ | ||
1046 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ | ||
1047 | }; | ||
1048 | |||
1049 | void __init s3c2442_init_irq(void) | ||
1050 | { | ||
1051 | pr_info("S3C2442: IRQ Support\n"); | ||
1052 | |||
1053 | #ifdef CONFIG_FIQ | ||
1054 | init_FIQ(FIQ_START); | ||
1055 | #endif | ||
1056 | |||
1057 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, | ||
1058 | 0x4a000000); | ||
1059 | if (IS_ERR(s3c_intc[0])) { | ||
1060 | pr_err("irq: could not create main interrupt controller\n"); | ||
1061 | return; | ||
1062 | } | ||
1063 | |||
1064 | s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); | ||
1065 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0], | ||
1066 | s3c_intc[0], 0x4a000018); | ||
1067 | } | ||
1068 | #endif | ||
1069 | |||
1070 | #ifdef CONFIG_CPU_S3C2443 | ||
1071 | static struct s3c_irq_data init_s3c2443base[32] = { | ||
1072 | { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ | ||
1073 | { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ | ||
1074 | { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ | ||
1075 | { .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ | ||
1076 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ | ||
1077 | { .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ | ||
1078 | { .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ | ||
1079 | { .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ | ||
1080 | { .type = S3C_IRQTYPE_EDGE, }, /* TICK */ | ||
1081 | { .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ | ||
1082 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ | ||
1083 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ | ||
1084 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ | ||
1085 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ | ||
1086 | { .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ | ||
1087 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ | ||
1088 | { .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ | ||
1089 | { .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ | ||
1090 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ | ||
1091 | { .type = S3C_IRQTYPE_EDGE, }, /* CFON */ | ||
1092 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ | ||
1093 | { .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ | ||
1094 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ | ||
1095 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ | ||
1096 | { .type = S3C_IRQTYPE_EDGE, }, /* NAND */ | ||
1097 | { .type = S3C_IRQTYPE_EDGE, }, /* USBD */ | ||
1098 | { .type = S3C_IRQTYPE_EDGE, }, /* USBH */ | ||
1099 | { .type = S3C_IRQTYPE_EDGE, }, /* IIC */ | ||
1100 | { .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ | ||
1101 | { .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ | ||
1102 | { .type = S3C_IRQTYPE_EDGE, }, /* RTC */ | ||
1103 | { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ | ||
1104 | }; | ||
1105 | |||
1106 | |||
1107 | static struct s3c_irq_data init_s3c2443subint[32] = { | ||
1108 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ | ||
1109 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ | ||
1110 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ | ||
1111 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ | ||
1112 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ | ||
1113 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ | ||
1114 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ | ||
1115 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ | ||
1116 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ | ||
1117 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ | ||
1118 | { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ | ||
1119 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ | ||
1120 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ | ||
1121 | { .type = S3C_IRQTYPE_NONE }, /* reserved */ | ||
1122 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD1 */ | ||
1123 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ | ||
1124 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ | ||
1125 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ | ||
1126 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ | ||
1127 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ | ||
1128 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ | ||
1129 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ | ||
1130 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ | ||
1131 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ | ||
1132 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ | ||
1133 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ | ||
1134 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ | ||
1135 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ | ||
1136 | { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ | ||
1137 | }; | ||
1138 | |||
1139 | void __init s3c2443_init_irq(void) | ||
1140 | { | ||
1141 | pr_info("S3C2443: IRQ Support\n"); | ||
1142 | |||
1143 | #ifdef CONFIG_FIQ | ||
1144 | init_FIQ(FIQ_START); | ||
1145 | #endif | ||
1146 | |||
1147 | s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, | ||
1148 | 0x4a000000); | ||
1149 | if (IS_ERR(s3c_intc[0])) { | ||
1150 | pr_err("irq: could not create main interrupt controller\n"); | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); | ||
1155 | s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0], | ||
1156 | s3c_intc[0], 0x4a000018); | ||
1157 | } | ||
1158 | #endif | ||
1159 | |||
1160 | #ifdef CONFIG_OF | ||
1161 | static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, | ||
1162 | irq_hw_number_t hw) | ||
1163 | { | ||
1164 | unsigned int ctrl_num = hw / 32; | ||
1165 | unsigned int intc_hw = hw % 32; | ||
1166 | struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; | ||
1167 | struct s3c_irq_intc *parent_intc = intc->parent; | ||
1168 | struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; | ||
1169 | |||
1170 | /* attach controller pointer to irq_data */ | ||
1171 | irq_data->intc = intc; | ||
1172 | irq_data->offset = intc_hw; | ||
1173 | |||
1174 | if (!parent_intc) | ||
1175 | irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq); | ||
1176 | else | ||
1177 | irq_set_chip_and_handler(virq, &s3c_irq_level_chip, | ||
1178 | handle_edge_irq); | ||
1179 | |||
1180 | irq_set_chip_data(virq, irq_data); | ||
1181 | |||
1182 | set_irq_flags(virq, IRQF_VALID); | ||
1183 | |||
1184 | return 0; | ||
1185 | } | ||
1186 | |||
1187 | /* Translate our of irq notation | ||
1188 | * format: <ctrl_num ctrl_irq parent_irq type> | ||
1189 | */ | ||
1190 | static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, | ||
1191 | const u32 *intspec, unsigned int intsize, | ||
1192 | irq_hw_number_t *out_hwirq, unsigned int *out_type) | ||
1193 | { | ||
1194 | struct s3c_irq_intc *intc; | ||
1195 | struct s3c_irq_intc *parent_intc; | ||
1196 | struct s3c_irq_data *irq_data; | ||
1197 | struct s3c_irq_data *parent_irq_data; | ||
1198 | int irqno; | ||
1199 | |||
1200 | if (WARN_ON(intsize < 4)) | ||
1201 | return -EINVAL; | ||
1202 | |||
1203 | if (intspec[0] > 2 || !s3c_intc[intspec[0]]) { | ||
1204 | pr_err("controller number %d invalid\n", intspec[0]); | ||
1205 | return -EINVAL; | ||
1206 | } | ||
1207 | intc = s3c_intc[intspec[0]]; | ||
1208 | |||
1209 | *out_hwirq = intspec[0] * 32 + intspec[2]; | ||
1210 | *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; | ||
1211 | |||
1212 | parent_intc = intc->parent; | ||
1213 | if (parent_intc) { | ||
1214 | irq_data = &intc->irqs[intspec[2]]; | ||
1215 | irq_data->parent_irq = intspec[1]; | ||
1216 | parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; | ||
1217 | parent_irq_data->sub_intc = intc; | ||
1218 | parent_irq_data->sub_bits |= (1UL << intspec[2]); | ||
1219 | |||
1220 | /* parent_intc is always s3c_intc[0], so no offset */ | ||
1221 | irqno = irq_create_mapping(parent_intc->domain, intspec[1]); | ||
1222 | if (irqno < 0) { | ||
1223 | pr_err("irq: could not map parent interrupt\n"); | ||
1224 | return irqno; | ||
1225 | } | ||
1226 | |||
1227 | irq_set_chained_handler(irqno, s3c_irq_demux); | ||
1228 | } | ||
1229 | |||
1230 | return 0; | ||
1231 | } | ||
1232 | |||
1233 | static struct irq_domain_ops s3c24xx_irq_ops_of = { | ||
1234 | .map = s3c24xx_irq_map_of, | ||
1235 | .xlate = s3c24xx_irq_xlate_of, | ||
1236 | }; | ||
1237 | |||
1238 | struct s3c24xx_irq_of_ctrl { | ||
1239 | char *name; | ||
1240 | unsigned long offset; | ||
1241 | struct s3c_irq_intc **handle; | ||
1242 | struct s3c_irq_intc **parent; | ||
1243 | struct irq_domain_ops *ops; | ||
1244 | }; | ||
1245 | |||
1246 | static int __init s3c_init_intc_of(struct device_node *np, | ||
1247 | struct device_node *interrupt_parent, | ||
1248 | struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) | ||
1249 | { | ||
1250 | struct s3c_irq_intc *intc; | ||
1251 | struct s3c24xx_irq_of_ctrl *ctrl; | ||
1252 | struct irq_domain *domain; | ||
1253 | void __iomem *reg_base; | ||
1254 | int i; | ||
1255 | |||
1256 | reg_base = of_iomap(np, 0); | ||
1257 | if (!reg_base) { | ||
1258 | pr_err("irq-s3c24xx: could not map irq registers\n"); | ||
1259 | return -EINVAL; | ||
1260 | } | ||
1261 | |||
1262 | domain = irq_domain_add_linear(np, num_ctrl * 32, | ||
1263 | &s3c24xx_irq_ops_of, NULL); | ||
1264 | if (!domain) { | ||
1265 | pr_err("irq: could not create irq-domain\n"); | ||
1266 | return -EINVAL; | ||
1267 | } | ||
1268 | |||
1269 | for (i = 0; i < num_ctrl; i++) { | ||
1270 | ctrl = &s3c_ctrl[i]; | ||
1271 | |||
1272 | pr_debug("irq: found controller %s\n", ctrl->name); | ||
1273 | |||
1274 | intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); | ||
1275 | if (!intc) | ||
1276 | return -ENOMEM; | ||
1277 | |||
1278 | intc->domain = domain; | ||
1279 | intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, | ||
1280 | GFP_KERNEL); | ||
1281 | if (!intc->irqs) { | ||
1282 | kfree(intc); | ||
1283 | return -ENOMEM; | ||
1284 | } | ||
1285 | |||
1286 | if (ctrl->parent) { | ||
1287 | intc->reg_pending = reg_base + ctrl->offset; | ||
1288 | intc->reg_mask = reg_base + ctrl->offset + 0x4; | ||
1289 | |||
1290 | if (*(ctrl->parent)) { | ||
1291 | intc->parent = *(ctrl->parent); | ||
1292 | } else { | ||
1293 | pr_warn("irq: parent of %s missing\n", | ||
1294 | ctrl->name); | ||
1295 | kfree(intc->irqs); | ||
1296 | kfree(intc); | ||
1297 | continue; | ||
1298 | } | ||
1299 | } else { | ||
1300 | intc->reg_pending = reg_base + ctrl->offset; | ||
1301 | intc->reg_mask = reg_base + ctrl->offset + 0x08; | ||
1302 | intc->reg_intpnd = reg_base + ctrl->offset + 0x10; | ||
1303 | } | ||
1304 | |||
1305 | s3c24xx_clear_intc(intc); | ||
1306 | s3c_intc[i] = intc; | ||
1307 | } | ||
1308 | |||
1309 | set_handle_irq(s3c24xx_handle_irq); | ||
1310 | |||
1311 | return 0; | ||
1312 | } | ||
1313 | |||
1314 | static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { | ||
1315 | { | ||
1316 | .name = "intc", | ||
1317 | .offset = 0, | ||
1318 | }, { | ||
1319 | .name = "subintc", | ||
1320 | .offset = 0x18, | ||
1321 | .parent = &s3c_intc[0], | ||
1322 | } | ||
1323 | }; | ||
1324 | |||
1325 | int __init s3c2410_init_intc_of(struct device_node *np, | ||
1326 | struct device_node *interrupt_parent, | ||
1327 | struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) | ||
1328 | { | ||
1329 | return s3c_init_intc_of(np, interrupt_parent, | ||
1330 | s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); | ||
1331 | } | ||
1332 | IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of); | ||
1333 | |||
1334 | static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { | ||
1335 | { | ||
1336 | .name = "intc", | ||
1337 | .offset = 0, | ||
1338 | }, { | ||
1339 | .name = "subintc", | ||
1340 | .offset = 0x18, | ||
1341 | .parent = &s3c_intc[0], | ||
1342 | }, { | ||
1343 | .name = "intc2", | ||
1344 | .offset = 0x40, | ||
1345 | } | ||
1346 | }; | ||
1347 | |||
1348 | int __init s3c2416_init_intc_of(struct device_node *np, | ||
1349 | struct device_node *interrupt_parent, | ||
1350 | struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) | ||
1351 | { | ||
1352 | return s3c_init_intc_of(np, interrupt_parent, | ||
1353 | s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); | ||
1354 | } | ||
1355 | IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of); | ||
1356 | #endif | ||
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c new file mode 100644 index 000000000000..d97059550a2c --- /dev/null +++ b/drivers/irqchip/irq-vt8500.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-vt8500/irq.c | ||
3 | * | ||
4 | * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> | ||
5 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * This file is copied and modified from the original irq.c provided by | ||
24 | * Alexey Charkov. Minor changes have been made for Device Tree Support. | ||
25 | */ | ||
26 | |||
27 | #include <linux/slab.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/irq.h> | ||
30 | #include <linux/irqdomain.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/bitops.h> | ||
33 | |||
34 | #include <linux/of.h> | ||
35 | #include <linux/of_irq.h> | ||
36 | #include <linux/of_address.h> | ||
37 | |||
38 | #include <asm/irq.h> | ||
39 | #include <asm/exception.h> | ||
40 | #include <asm/mach/irq.h> | ||
41 | |||
42 | #include "irqchip.h" | ||
43 | |||
44 | #define VT8500_ICPC_IRQ 0x20 | ||
45 | #define VT8500_ICPC_FIQ 0x24 | ||
46 | #define VT8500_ICDC 0x40 /* Destination Control 64*u32 */ | ||
47 | #define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */ | ||
48 | |||
49 | /* ICPC */ | ||
50 | #define ICPC_MASK 0x3F | ||
51 | #define ICPC_ROTATE BIT(6) | ||
52 | |||
53 | /* IC_DCTR */ | ||
54 | #define ICDC_IRQ 0x00 | ||
55 | #define ICDC_FIQ 0x01 | ||
56 | #define ICDC_DSS0 0x02 | ||
57 | #define ICDC_DSS1 0x03 | ||
58 | #define ICDC_DSS2 0x04 | ||
59 | #define ICDC_DSS3 0x05 | ||
60 | #define ICDC_DSS4 0x06 | ||
61 | #define ICDC_DSS5 0x07 | ||
62 | |||
63 | #define VT8500_INT_DISABLE 0 | ||
64 | #define VT8500_INT_ENABLE BIT(3) | ||
65 | |||
66 | #define VT8500_TRIGGER_HIGH 0 | ||
67 | #define VT8500_TRIGGER_RISING BIT(5) | ||
68 | #define VT8500_TRIGGER_FALLING BIT(6) | ||
69 | #define VT8500_EDGE ( VT8500_TRIGGER_RISING \ | ||
70 | | VT8500_TRIGGER_FALLING) | ||
71 | |||
72 | /* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ | ||
73 | #define VT8500_INTC_MAX 2 | ||
74 | |||
75 | struct vt8500_irq_data { | ||
76 | void __iomem *base; /* IO Memory base address */ | ||
77 | struct irq_domain *domain; /* Domain for this controller */ | ||
78 | }; | ||
79 | |||
80 | /* Global variable for accessing io-mem addresses */ | ||
81 | static struct vt8500_irq_data intc[VT8500_INTC_MAX]; | ||
82 | static u32 active_cnt = 0; | ||
83 | |||
84 | static void vt8500_irq_mask(struct irq_data *d) | ||
85 | { | ||
86 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
87 | void __iomem *base = priv->base; | ||
88 | void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); | ||
89 | u8 edge, dctr; | ||
90 | u32 status; | ||
91 | |||
92 | edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; | ||
93 | if (edge) { | ||
94 | status = readl(stat_reg); | ||
95 | |||
96 | status |= (1 << (d->hwirq & 0x1f)); | ||
97 | writel(status, stat_reg); | ||
98 | } else { | ||
99 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
100 | dctr &= ~VT8500_INT_ENABLE; | ||
101 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static void vt8500_irq_unmask(struct irq_data *d) | ||
106 | { | ||
107 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
108 | void __iomem *base = priv->base; | ||
109 | u8 dctr; | ||
110 | |||
111 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
112 | dctr |= VT8500_INT_ENABLE; | ||
113 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
114 | } | ||
115 | |||
116 | static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) | ||
117 | { | ||
118 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
119 | void __iomem *base = priv->base; | ||
120 | u8 dctr; | ||
121 | |||
122 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
123 | dctr &= ~VT8500_EDGE; | ||
124 | |||
125 | switch (flow_type) { | ||
126 | case IRQF_TRIGGER_LOW: | ||
127 | return -EINVAL; | ||
128 | case IRQF_TRIGGER_HIGH: | ||
129 | dctr |= VT8500_TRIGGER_HIGH; | ||
130 | __irq_set_handler_locked(d->irq, handle_level_irq); | ||
131 | break; | ||
132 | case IRQF_TRIGGER_FALLING: | ||
133 | dctr |= VT8500_TRIGGER_FALLING; | ||
134 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
135 | break; | ||
136 | case IRQF_TRIGGER_RISING: | ||
137 | dctr |= VT8500_TRIGGER_RISING; | ||
138 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
139 | break; | ||
140 | } | ||
141 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct irq_chip vt8500_irq_chip = { | ||
147 | .name = "vt8500", | ||
148 | .irq_ack = vt8500_irq_mask, | ||
149 | .irq_mask = vt8500_irq_mask, | ||
150 | .irq_unmask = vt8500_irq_unmask, | ||
151 | .irq_set_type = vt8500_irq_set_type, | ||
152 | }; | ||
153 | |||
154 | static void __init vt8500_init_irq_hw(void __iomem *base) | ||
155 | { | ||
156 | u32 i; | ||
157 | |||
158 | /* Enable rotating priority for IRQ */ | ||
159 | writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); | ||
160 | writel(0x00, base + VT8500_ICPC_FIQ); | ||
161 | |||
162 | /* Disable all interrupts and route them to IRQ */ | ||
163 | for (i = 0; i < 64; i++) | ||
164 | writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); | ||
165 | } | ||
166 | |||
167 | static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, | ||
168 | irq_hw_number_t hw) | ||
169 | { | ||
170 | irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); | ||
171 | set_irq_flags(virq, IRQF_VALID); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static struct irq_domain_ops vt8500_irq_domain_ops = { | ||
177 | .map = vt8500_irq_map, | ||
178 | .xlate = irq_domain_xlate_onecell, | ||
179 | }; | ||
180 | |||
181 | asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) | ||
182 | { | ||
183 | u32 stat, i; | ||
184 | int irqnr, virq; | ||
185 | void __iomem *base; | ||
186 | |||
187 | /* Loop through each active controller */ | ||
188 | for (i=0; i<active_cnt; i++) { | ||
189 | base = intc[i].base; | ||
190 | irqnr = readl_relaxed(base) & 0x3F; | ||
191 | /* | ||
192 | Highest Priority register default = 63, so check that this | ||
193 | is a real interrupt by checking the status register | ||
194 | */ | ||
195 | if (irqnr == 63) { | ||
196 | stat = readl_relaxed(base + VT8500_ICIS + 4); | ||
197 | if (!(stat & BIT(31))) | ||
198 | continue; | ||
199 | } | ||
200 | |||
201 | virq = irq_find_mapping(intc[i].domain, irqnr); | ||
202 | handle_IRQ(virq, regs); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) | ||
207 | { | ||
208 | int irq, i; | ||
209 | struct device_node *np = node; | ||
210 | |||
211 | if (active_cnt == VT8500_INTC_MAX) { | ||
212 | pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", | ||
213 | __func__); | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | intc[active_cnt].base = of_iomap(np, 0); | ||
218 | intc[active_cnt].domain = irq_domain_add_linear(node, 64, | ||
219 | &vt8500_irq_domain_ops, &intc[active_cnt]); | ||
220 | |||
221 | if (!intc[active_cnt].base) { | ||
222 | pr_err("%s: Unable to map IO memory\n", __func__); | ||
223 | goto out; | ||
224 | } | ||
225 | |||
226 | if (!intc[active_cnt].domain) { | ||
227 | pr_err("%s: Unable to add irq domain!\n", __func__); | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | set_handle_irq(vt8500_handle_irq); | ||
232 | |||
233 | vt8500_init_irq_hw(intc[active_cnt].base); | ||
234 | |||
235 | pr_info("vt8500-irq: Added interrupt controller\n"); | ||
236 | |||
237 | active_cnt++; | ||
238 | |||
239 | /* check if this is a slaved controller */ | ||
240 | if (of_irq_count(np) != 0) { | ||
241 | /* check that we have the correct number of interrupts */ | ||
242 | if (of_irq_count(np) != 8) { | ||
243 | pr_err("%s: Incorrect IRQ map for slaved controller\n", | ||
244 | __func__); | ||
245 | return -EINVAL; | ||
246 | } | ||
247 | |||
248 | for (i = 0; i < 8; i++) { | ||
249 | irq = irq_of_parse_and_map(np, i); | ||
250 | enable_irq(irq); | ||
251 | } | ||
252 | |||
253 | pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); | ||
254 | } | ||
255 | out: | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init); | ||