diff options
-rw-r--r-- | arch/sh/kernel/irq.c | 2 | ||||
-rw-r--r-- | drivers/sh/intc.c | 404 | ||||
-rw-r--r-- | include/linux/sh_intc.h | 12 |
3 files changed, 351 insertions, 67 deletions
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 257de1f0692b..c8e1409f20a2 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c | |||
@@ -283,6 +283,8 @@ void __init init_IRQ(void) | |||
283 | if (sh_mv.mv_init_irq) | 283 | if (sh_mv.mv_init_irq) |
284 | sh_mv.mv_init_irq(); | 284 | sh_mv.mv_init_irq(); |
285 | 285 | ||
286 | intc_finalize(); | ||
287 | |||
286 | irq_ctx_init(smp_processor_id()); | 288 | irq_ctx_init(smp_processor_id()); |
287 | } | 289 | } |
288 | 290 | ||
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index a27dcb4254c7..c81fe23db7f7 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/seq_file.h> | 35 | #include <linux/seq_file.h> |
36 | #include <linux/radix-tree.h> | 36 | #include <linux/radix-tree.h> |
37 | #include <linux/mutex.h> | 37 | #include <linux/mutex.h> |
38 | #include <linux/rcupdate.h> | ||
38 | #include <asm/sizes.h> | 39 | #include <asm/sizes.h> |
39 | 40 | ||
40 | #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ | 41 | #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ |
@@ -64,11 +65,19 @@ struct intc_map_entry { | |||
64 | struct intc_desc_int *desc; | 65 | struct intc_desc_int *desc; |
65 | }; | 66 | }; |
66 | 67 | ||
68 | struct intc_subgroup_entry { | ||
69 | unsigned int pirq; | ||
70 | intc_enum enum_id; | ||
71 | unsigned long handle; | ||
72 | }; | ||
73 | |||
67 | struct intc_desc_int { | 74 | struct intc_desc_int { |
68 | struct list_head list; | 75 | struct list_head list; |
69 | struct sys_device sysdev; | 76 | struct sys_device sysdev; |
70 | struct radix_tree_root tree; | 77 | struct radix_tree_root tree; |
71 | pm_message_t state; | 78 | pm_message_t state; |
79 | spinlock_t lock; | ||
80 | unsigned int index; | ||
72 | unsigned long *reg; | 81 | unsigned long *reg; |
73 | #ifdef CONFIG_SMP | 82 | #ifdef CONFIG_SMP |
74 | unsigned long *smp; | 83 | unsigned long *smp; |
@@ -84,6 +93,7 @@ struct intc_desc_int { | |||
84 | }; | 93 | }; |
85 | 94 | ||
86 | static LIST_HEAD(intc_list); | 95 | static LIST_HEAD(intc_list); |
96 | static unsigned int nr_intc_controllers; | ||
87 | 97 | ||
88 | /* | 98 | /* |
89 | * The intc_irq_map provides a global map of bound IRQ vectors for a | 99 | * The intc_irq_map provides a global map of bound IRQ vectors for a |
@@ -99,7 +109,7 @@ static LIST_HEAD(intc_list); | |||
99 | static DECLARE_BITMAP(intc_irq_map, NR_IRQS); | 109 | static DECLARE_BITMAP(intc_irq_map, NR_IRQS); |
100 | static struct intc_map_entry intc_irq_xlate[NR_IRQS]; | 110 | static struct intc_map_entry intc_irq_xlate[NR_IRQS]; |
101 | static DEFINE_SPINLOCK(vector_lock); | 111 | static DEFINE_SPINLOCK(vector_lock); |
102 | static DEFINE_MUTEX(irq_xlate_mutex); | 112 | static DEFINE_SPINLOCK(xlate_lock); |
103 | 113 | ||
104 | #ifdef CONFIG_SMP | 114 | #ifdef CONFIG_SMP |
105 | #define IS_SMP(x) x.smp | 115 | #define IS_SMP(x) x.smp |
@@ -118,12 +128,39 @@ static unsigned long ack_handle[NR_IRQS]; | |||
118 | static unsigned long dist_handle[NR_IRQS]; | 128 | static unsigned long dist_handle[NR_IRQS]; |
119 | #endif | 129 | #endif |
120 | 130 | ||
131 | struct intc_virq_list { | ||
132 | unsigned int irq; | ||
133 | struct intc_virq_list *next; | ||
134 | }; | ||
135 | |||
136 | #define for_each_virq(entry, head) \ | ||
137 | for (entry = head; entry; entry = entry->next) | ||
138 | |||
121 | static inline struct intc_desc_int *get_intc_desc(unsigned int irq) | 139 | static inline struct intc_desc_int *get_intc_desc(unsigned int irq) |
122 | { | 140 | { |
123 | struct irq_chip *chip = get_irq_chip(irq); | 141 | struct irq_chip *chip = get_irq_chip(irq); |
142 | |||
124 | return container_of(chip, struct intc_desc_int, chip); | 143 | return container_of(chip, struct intc_desc_int, chip); |
125 | } | 144 | } |
126 | 145 | ||
146 | static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) | ||
147 | { | ||
148 | generic_handle_irq((unsigned int)get_irq_data(irq)); | ||
149 | } | ||
150 | |||
151 | static inline void activate_irq(int irq) | ||
152 | { | ||
153 | #ifdef CONFIG_ARM | ||
154 | /* ARM requires an extra step to clear IRQ_NOREQUEST, which it | ||
155 | * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. | ||
156 | */ | ||
157 | set_irq_flags(irq, IRQF_VALID); | ||
158 | #else | ||
159 | /* same effect on other architectures */ | ||
160 | set_irq_noprobe(irq); | ||
161 | #endif | ||
162 | } | ||
163 | |||
127 | static unsigned long intc_phys_to_virt(struct intc_desc_int *d, | 164 | static unsigned long intc_phys_to_virt(struct intc_desc_int *d, |
128 | unsigned long address) | 165 | unsigned long address) |
129 | { | 166 | { |
@@ -177,56 +214,103 @@ static inline unsigned int set_field(unsigned int value, | |||
177 | return value; | 214 | return value; |
178 | } | 215 | } |
179 | 216 | ||
180 | static void write_8(unsigned long addr, unsigned long h, unsigned long data) | 217 | static inline unsigned long get_field(unsigned int value, unsigned int handle) |
218 | { | ||
219 | unsigned int width = _INTC_WIDTH(handle); | ||
220 | unsigned int shift = _INTC_SHIFT(handle); | ||
221 | unsigned int mask = ((1 << width) - 1) << shift; | ||
222 | |||
223 | return (value & mask) >> shift; | ||
224 | } | ||
225 | |||
226 | static unsigned long test_8(unsigned long addr, unsigned long h, | ||
227 | unsigned long ignore) | ||
228 | { | ||
229 | return get_field(__raw_readb(addr), h); | ||
230 | } | ||
231 | |||
232 | static unsigned long test_16(unsigned long addr, unsigned long h, | ||
233 | unsigned long ignore) | ||
234 | { | ||
235 | return get_field(__raw_readw(addr), h); | ||
236 | } | ||
237 | |||
238 | static unsigned long test_32(unsigned long addr, unsigned long h, | ||
239 | unsigned long ignore) | ||
240 | { | ||
241 | return get_field(__raw_readl(addr), h); | ||
242 | } | ||
243 | |||
244 | static unsigned long write_8(unsigned long addr, unsigned long h, | ||
245 | unsigned long data) | ||
181 | { | 246 | { |
182 | __raw_writeb(set_field(0, data, h), addr); | 247 | __raw_writeb(set_field(0, data, h), addr); |
183 | (void)__raw_readb(addr); /* Defeat write posting */ | 248 | (void)__raw_readb(addr); /* Defeat write posting */ |
249 | return 0; | ||
184 | } | 250 | } |
185 | 251 | ||
186 | static void write_16(unsigned long addr, unsigned long h, unsigned long data) | 252 | static unsigned long write_16(unsigned long addr, unsigned long h, |
253 | unsigned long data) | ||
187 | { | 254 | { |
188 | __raw_writew(set_field(0, data, h), addr); | 255 | __raw_writew(set_field(0, data, h), addr); |
189 | (void)__raw_readw(addr); /* Defeat write posting */ | 256 | (void)__raw_readw(addr); /* Defeat write posting */ |
257 | return 0; | ||
190 | } | 258 | } |
191 | 259 | ||
192 | static void write_32(unsigned long addr, unsigned long h, unsigned long data) | 260 | static unsigned long write_32(unsigned long addr, unsigned long h, |
261 | unsigned long data) | ||
193 | { | 262 | { |
194 | __raw_writel(set_field(0, data, h), addr); | 263 | __raw_writel(set_field(0, data, h), addr); |
195 | (void)__raw_readl(addr); /* Defeat write posting */ | 264 | (void)__raw_readl(addr); /* Defeat write posting */ |
265 | return 0; | ||
196 | } | 266 | } |
197 | 267 | ||
198 | static void modify_8(unsigned long addr, unsigned long h, unsigned long data) | 268 | static unsigned long modify_8(unsigned long addr, unsigned long h, |
269 | unsigned long data) | ||
199 | { | 270 | { |
200 | unsigned long flags; | 271 | unsigned long flags; |
201 | local_irq_save(flags); | 272 | local_irq_save(flags); |
202 | __raw_writeb(set_field(__raw_readb(addr), data, h), addr); | 273 | __raw_writeb(set_field(__raw_readb(addr), data, h), addr); |
203 | (void)__raw_readb(addr); /* Defeat write posting */ | 274 | (void)__raw_readb(addr); /* Defeat write posting */ |
204 | local_irq_restore(flags); | 275 | local_irq_restore(flags); |
276 | return 0; | ||
205 | } | 277 | } |
206 | 278 | ||
207 | static void modify_16(unsigned long addr, unsigned long h, unsigned long data) | 279 | static unsigned long modify_16(unsigned long addr, unsigned long h, |
280 | unsigned long data) | ||
208 | { | 281 | { |
209 | unsigned long flags; | 282 | unsigned long flags; |
210 | local_irq_save(flags); | 283 | local_irq_save(flags); |
211 | __raw_writew(set_field(__raw_readw(addr), data, h), addr); | 284 | __raw_writew(set_field(__raw_readw(addr), data, h), addr); |
212 | (void)__raw_readw(addr); /* Defeat write posting */ | 285 | (void)__raw_readw(addr); /* Defeat write posting */ |
213 | local_irq_restore(flags); | 286 | local_irq_restore(flags); |
287 | return 0; | ||
214 | } | 288 | } |
215 | 289 | ||
216 | static void modify_32(unsigned long addr, unsigned long h, unsigned long data) | 290 | static unsigned long modify_32(unsigned long addr, unsigned long h, |
291 | unsigned long data) | ||
217 | { | 292 | { |
218 | unsigned long flags; | 293 | unsigned long flags; |
219 | local_irq_save(flags); | 294 | local_irq_save(flags); |
220 | __raw_writel(set_field(__raw_readl(addr), data, h), addr); | 295 | __raw_writel(set_field(__raw_readl(addr), data, h), addr); |
221 | (void)__raw_readl(addr); /* Defeat write posting */ | 296 | (void)__raw_readl(addr); /* Defeat write posting */ |
222 | local_irq_restore(flags); | 297 | local_irq_restore(flags); |
298 | return 0; | ||
223 | } | 299 | } |
224 | 300 | ||
225 | enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; | 301 | enum { |
302 | REG_FN_ERR = 0, | ||
303 | REG_FN_TEST_BASE = 1, | ||
304 | REG_FN_WRITE_BASE = 5, | ||
305 | REG_FN_MODIFY_BASE = 9 | ||
306 | }; | ||
226 | 307 | ||
227 | static void (*intc_reg_fns[])(unsigned long addr, | 308 | static unsigned long (*intc_reg_fns[])(unsigned long addr, |
228 | unsigned long h, | 309 | unsigned long h, |
229 | unsigned long data) = { | 310 | unsigned long data) = { |
311 | [REG_FN_TEST_BASE + 0] = test_8, | ||
312 | [REG_FN_TEST_BASE + 1] = test_16, | ||
313 | [REG_FN_TEST_BASE + 3] = test_32, | ||
230 | [REG_FN_WRITE_BASE + 0] = write_8, | 314 | [REG_FN_WRITE_BASE + 0] = write_8, |
231 | [REG_FN_WRITE_BASE + 1] = write_16, | 315 | [REG_FN_WRITE_BASE + 1] = write_16, |
232 | [REG_FN_WRITE_BASE + 3] = write_32, | 316 | [REG_FN_WRITE_BASE + 3] = write_32, |
@@ -242,42 +326,42 @@ enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ | |||
242 | MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ | 326 | MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ |
243 | }; | 327 | }; |
244 | 328 | ||
245 | static void intc_mode_field(unsigned long addr, | 329 | static unsigned long intc_mode_field(unsigned long addr, |
246 | unsigned long handle, | 330 | unsigned long handle, |
247 | void (*fn)(unsigned long, | 331 | unsigned long (*fn)(unsigned long, |
248 | unsigned long, | 332 | unsigned long, |
249 | unsigned long), | 333 | unsigned long), |
250 | unsigned int irq) | 334 | unsigned int irq) |
251 | { | 335 | { |
252 | fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); | 336 | return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); |
253 | } | 337 | } |
254 | 338 | ||
255 | static void intc_mode_zero(unsigned long addr, | 339 | static unsigned long intc_mode_zero(unsigned long addr, |
256 | unsigned long handle, | 340 | unsigned long handle, |
257 | void (*fn)(unsigned long, | 341 | unsigned long (*fn)(unsigned long, |
258 | unsigned long, | 342 | unsigned long, |
259 | unsigned long), | 343 | unsigned long), |
260 | unsigned int irq) | 344 | unsigned int irq) |
261 | { | 345 | { |
262 | fn(addr, handle, 0); | 346 | return fn(addr, handle, 0); |
263 | } | 347 | } |
264 | 348 | ||
265 | static void intc_mode_prio(unsigned long addr, | 349 | static unsigned long intc_mode_prio(unsigned long addr, |
266 | unsigned long handle, | 350 | unsigned long handle, |
267 | void (*fn)(unsigned long, | 351 | unsigned long (*fn)(unsigned long, |
268 | unsigned long, | 352 | unsigned long, |
269 | unsigned long), | 353 | unsigned long), |
270 | unsigned int irq) | 354 | unsigned int irq) |
271 | { | 355 | { |
272 | fn(addr, handle, intc_prio_level[irq]); | 356 | return fn(addr, handle, intc_prio_level[irq]); |
273 | } | 357 | } |
274 | 358 | ||
275 | static void (*intc_enable_fns[])(unsigned long addr, | 359 | static unsigned long (*intc_enable_fns[])(unsigned long addr, |
276 | unsigned long handle, | 360 | unsigned long handle, |
277 | void (*fn)(unsigned long, | 361 | unsigned long (*fn)(unsigned long, |
278 | unsigned long, | 362 | unsigned long, |
279 | unsigned long), | 363 | unsigned long), |
280 | unsigned int irq) = { | 364 | unsigned int irq) = { |
281 | [MODE_ENABLE_REG] = intc_mode_field, | 365 | [MODE_ENABLE_REG] = intc_mode_field, |
282 | [MODE_MASK_REG] = intc_mode_zero, | 366 | [MODE_MASK_REG] = intc_mode_zero, |
283 | [MODE_DUAL_REG] = intc_mode_field, | 367 | [MODE_DUAL_REG] = intc_mode_field, |
@@ -285,9 +369,9 @@ static void (*intc_enable_fns[])(unsigned long addr, | |||
285 | [MODE_PCLR_REG] = intc_mode_prio, | 369 | [MODE_PCLR_REG] = intc_mode_prio, |
286 | }; | 370 | }; |
287 | 371 | ||
288 | static void (*intc_disable_fns[])(unsigned long addr, | 372 | static unsigned long (*intc_disable_fns[])(unsigned long addr, |
289 | unsigned long handle, | 373 | unsigned long handle, |
290 | void (*fn)(unsigned long, | 374 | unsigned long (*fn)(unsigned long, |
291 | unsigned long, | 375 | unsigned long, |
292 | unsigned long), | 376 | unsigned long), |
293 | unsigned int irq) = { | 377 | unsigned int irq) = { |
@@ -421,12 +505,13 @@ static void intc_disable(unsigned int irq) | |||
421 | } | 505 | } |
422 | } | 506 | } |
423 | 507 | ||
424 | static void (*intc_enable_noprio_fns[])(unsigned long addr, | 508 | static unsigned long |
425 | unsigned long handle, | 509 | (*intc_enable_noprio_fns[])(unsigned long addr, |
426 | void (*fn)(unsigned long, | 510 | unsigned long handle, |
427 | unsigned long, | 511 | unsigned long (*fn)(unsigned long, |
428 | unsigned long), | 512 | unsigned long, |
429 | unsigned int irq) = { | 513 | unsigned long), |
514 | unsigned int irq) = { | ||
430 | [MODE_ENABLE_REG] = intc_mode_field, | 515 | [MODE_ENABLE_REG] = intc_mode_field, |
431 | [MODE_MASK_REG] = intc_mode_zero, | 516 | [MODE_MASK_REG] = intc_mode_zero, |
432 | [MODE_DUAL_REG] = intc_mode_field, | 517 | [MODE_DUAL_REG] = intc_mode_field, |
@@ -439,8 +524,9 @@ static void intc_enable_disable(struct intc_desc_int *d, | |||
439 | { | 524 | { |
440 | unsigned long addr; | 525 | unsigned long addr; |
441 | unsigned int cpu; | 526 | unsigned int cpu; |
442 | void (*fn)(unsigned long, unsigned long, | 527 | unsigned long (*fn)(unsigned long, unsigned long, |
443 | void (*)(unsigned long, unsigned long, unsigned long), | 528 | unsigned long (*)(unsigned long, unsigned long, |
529 | unsigned long), | ||
444 | unsigned int); | 530 | unsigned int); |
445 | 531 | ||
446 | if (do_enable) { | 532 | if (do_enable) { |
@@ -861,6 +947,186 @@ unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id) | |||
861 | } | 947 | } |
862 | EXPORT_SYMBOL_GPL(intc_irq_lookup); | 948 | EXPORT_SYMBOL_GPL(intc_irq_lookup); |
863 | 949 | ||
950 | static int add_virq_to_pirq(unsigned int irq, unsigned int virq) | ||
951 | { | ||
952 | struct intc_virq_list **last, *entry; | ||
953 | struct irq_desc *desc = irq_to_desc(irq); | ||
954 | |||
955 | /* scan for duplicates */ | ||
956 | last = (struct intc_virq_list **)&desc->handler_data; | ||
957 | for_each_virq(entry, desc->handler_data) { | ||
958 | if (entry->irq == virq) | ||
959 | return 0; | ||
960 | last = &entry->next; | ||
961 | } | ||
962 | |||
963 | entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC); | ||
964 | if (!entry) { | ||
965 | pr_err("can't allocate VIRQ mapping for %d\n", virq); | ||
966 | return -ENOMEM; | ||
967 | } | ||
968 | |||
969 | entry->irq = virq; | ||
970 | |||
971 | *last = entry; | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) | ||
977 | { | ||
978 | struct intc_virq_list *entry, *vlist = get_irq_data(irq); | ||
979 | struct intc_desc_int *d = get_intc_desc(irq); | ||
980 | |||
981 | desc->chip->mask_ack(irq); | ||
982 | |||
983 | for_each_virq(entry, vlist) { | ||
984 | unsigned long addr, handle; | ||
985 | |||
986 | handle = (unsigned long)get_irq_data(entry->irq); | ||
987 | addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); | ||
988 | |||
989 | if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) | ||
990 | generic_handle_irq(entry->irq); | ||
991 | } | ||
992 | |||
993 | desc->chip->unmask(irq); | ||
994 | } | ||
995 | |||
996 | static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup, | ||
997 | struct intc_desc_int *d, | ||
998 | unsigned int index) | ||
999 | { | ||
1000 | unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1; | ||
1001 | |||
1002 | return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg), | ||
1003 | 0, 1, (subgroup->reg_width - 1) - index); | ||
1004 | } | ||
1005 | |||
1006 | #define INTC_TAG_VIRQ_NEEDS_ALLOC 0 | ||
1007 | |||
1008 | static void __init intc_subgroup_init_one(struct intc_desc *desc, | ||
1009 | struct intc_desc_int *d, | ||
1010 | struct intc_subgroup *subgroup) | ||
1011 | { | ||
1012 | struct intc_map_entry *mapped; | ||
1013 | unsigned int pirq; | ||
1014 | unsigned long flags; | ||
1015 | int i; | ||
1016 | |||
1017 | mapped = radix_tree_lookup(&d->tree, subgroup->parent_id); | ||
1018 | if (!mapped) { | ||
1019 | WARN_ON(1); | ||
1020 | return; | ||
1021 | } | ||
1022 | |||
1023 | pirq = mapped - intc_irq_xlate; | ||
1024 | |||
1025 | spin_lock_irqsave(&d->lock, flags); | ||
1026 | |||
1027 | for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) { | ||
1028 | struct intc_subgroup_entry *entry; | ||
1029 | int err; | ||
1030 | |||
1031 | if (!subgroup->enum_ids[i]) | ||
1032 | continue; | ||
1033 | |||
1034 | entry = kmalloc(sizeof(*entry), GFP_NOWAIT); | ||
1035 | if (!entry) | ||
1036 | break; | ||
1037 | |||
1038 | entry->pirq = pirq; | ||
1039 | entry->enum_id = subgroup->enum_ids[i]; | ||
1040 | entry->handle = intc_subgroup_data(subgroup, d, i); | ||
1041 | |||
1042 | err = radix_tree_insert(&d->tree, entry->enum_id, entry); | ||
1043 | if (unlikely(err < 0)) | ||
1044 | break; | ||
1045 | |||
1046 | radix_tree_tag_set(&d->tree, entry->enum_id, | ||
1047 | INTC_TAG_VIRQ_NEEDS_ALLOC); | ||
1048 | } | ||
1049 | |||
1050 | spin_unlock_irqrestore(&d->lock, flags); | ||
1051 | } | ||
1052 | |||
1053 | static void __init intc_subgroup_init(struct intc_desc *desc, | ||
1054 | struct intc_desc_int *d) | ||
1055 | { | ||
1056 | int i; | ||
1057 | |||
1058 | if (!desc->hw.subgroups) | ||
1059 | return; | ||
1060 | |||
1061 | for (i = 0; i < desc->hw.nr_subgroups; i++) | ||
1062 | intc_subgroup_init_one(desc, d, desc->hw.subgroups + i); | ||
1063 | } | ||
1064 | |||
1065 | static void __init intc_subgroup_map(struct intc_desc_int *d) | ||
1066 | { | ||
1067 | struct intc_subgroup_entry *entries[32]; | ||
1068 | unsigned long flags; | ||
1069 | unsigned int nr_found; | ||
1070 | int i; | ||
1071 | |||
1072 | spin_lock_irqsave(&d->lock, flags); | ||
1073 | |||
1074 | restart: | ||
1075 | nr_found = radix_tree_gang_lookup_tag_slot(&d->tree, | ||
1076 | (void ***)entries, 0, ARRAY_SIZE(entries), | ||
1077 | INTC_TAG_VIRQ_NEEDS_ALLOC); | ||
1078 | |||
1079 | for (i = 0; i < nr_found; i++) { | ||
1080 | struct intc_subgroup_entry *entry; | ||
1081 | int irq; | ||
1082 | |||
1083 | entry = radix_tree_deref_slot((void **)entries[i]); | ||
1084 | if (unlikely(!entry)) | ||
1085 | continue; | ||
1086 | if (unlikely(entry == RADIX_TREE_RETRY)) | ||
1087 | goto restart; | ||
1088 | |||
1089 | irq = create_irq(); | ||
1090 | if (unlikely(irq < 0)) { | ||
1091 | pr_err("no more free IRQs, bailing..\n"); | ||
1092 | break; | ||
1093 | } | ||
1094 | |||
1095 | pr_info("Setting up a chained VIRQ from %d -> %d\n", | ||
1096 | irq, entry->pirq); | ||
1097 | |||
1098 | spin_lock(&xlate_lock); | ||
1099 | intc_irq_xlate[irq].desc = d; | ||
1100 | intc_irq_xlate[irq].enum_id = entry->enum_id; | ||
1101 | spin_unlock(&xlate_lock); | ||
1102 | |||
1103 | set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq), | ||
1104 | handle_simple_irq, "virq"); | ||
1105 | set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); | ||
1106 | |||
1107 | set_irq_data(irq, (void *)entry->handle); | ||
1108 | |||
1109 | set_irq_chained_handler(entry->pirq, intc_virq_handler); | ||
1110 | add_virq_to_pirq(entry->pirq, irq); | ||
1111 | |||
1112 | radix_tree_tag_clear(&d->tree, entry->enum_id, | ||
1113 | INTC_TAG_VIRQ_NEEDS_ALLOC); | ||
1114 | radix_tree_replace_slot((void **)entries[i], | ||
1115 | &intc_irq_xlate[irq]); | ||
1116 | } | ||
1117 | |||
1118 | spin_unlock_irqrestore(&d->lock, flags); | ||
1119 | } | ||
1120 | |||
1121 | void __init intc_finalize(void) | ||
1122 | { | ||
1123 | struct intc_desc_int *d; | ||
1124 | |||
1125 | list_for_each_entry(d, &intc_list, list) | ||
1126 | if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC)) | ||
1127 | intc_subgroup_map(d); | ||
1128 | } | ||
1129 | |||
864 | static void __init intc_register_irq(struct intc_desc *desc, | 1130 | static void __init intc_register_irq(struct intc_desc *desc, |
865 | struct intc_desc_int *d, | 1131 | struct intc_desc_int *d, |
866 | intc_enum enum_id, | 1132 | intc_enum enum_id, |
@@ -868,6 +1134,7 @@ static void __init intc_register_irq(struct intc_desc *desc, | |||
868 | { | 1134 | { |
869 | struct intc_handle_int *hp; | 1135 | struct intc_handle_int *hp; |
870 | unsigned int data[2], primary; | 1136 | unsigned int data[2], primary; |
1137 | unsigned long flags; | ||
871 | 1138 | ||
872 | /* | 1139 | /* |
873 | * Register the IRQ position with the global IRQ map, then insert | 1140 | * Register the IRQ position with the global IRQ map, then insert |
@@ -875,9 +1142,9 @@ static void __init intc_register_irq(struct intc_desc *desc, | |||
875 | */ | 1142 | */ |
876 | set_bit(irq, intc_irq_map); | 1143 | set_bit(irq, intc_irq_map); |
877 | 1144 | ||
878 | mutex_lock(&irq_xlate_mutex); | 1145 | spin_lock_irqsave(&xlate_lock, flags); |
879 | radix_tree_insert(&d->tree, enum_id, &intc_irq_xlate[irq]); | 1146 | radix_tree_insert(&d->tree, enum_id, &intc_irq_xlate[irq]); |
880 | mutex_unlock(&irq_xlate_mutex); | 1147 | spin_unlock_irqrestore(&xlate_lock, flags); |
881 | 1148 | ||
882 | /* | 1149 | /* |
883 | * Prefer single interrupt source bitmap over other combinations: | 1150 | * Prefer single interrupt source bitmap over other combinations: |
@@ -957,9 +1224,7 @@ static void __init intc_register_irq(struct intc_desc *desc, | |||
957 | dist_handle[irq] = intc_dist_data(desc, d, enum_id); | 1224 | dist_handle[irq] = intc_dist_data(desc, d, enum_id); |
958 | #endif | 1225 | #endif |
959 | 1226 | ||
960 | #ifdef CONFIG_ARM | 1227 | activate_irq(irq); |
961 | set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ | ||
962 | #endif | ||
963 | } | 1228 | } |
964 | 1229 | ||
965 | static unsigned int __init save_reg(struct intc_desc_int *d, | 1230 | static unsigned int __init save_reg(struct intc_desc_int *d, |
@@ -980,11 +1245,6 @@ static unsigned int __init save_reg(struct intc_desc_int *d, | |||
980 | return 0; | 1245 | return 0; |
981 | } | 1246 | } |
982 | 1247 | ||
983 | static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) | ||
984 | { | ||
985 | generic_handle_irq((unsigned int)get_irq_data(irq)); | ||
986 | } | ||
987 | |||
988 | int __init register_intc_controller(struct intc_desc *desc) | 1248 | int __init register_intc_controller(struct intc_desc *desc) |
989 | { | 1249 | { |
990 | unsigned int i, k, smp; | 1250 | unsigned int i, k, smp; |
@@ -1000,7 +1260,11 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1000 | goto err0; | 1260 | goto err0; |
1001 | 1261 | ||
1002 | INIT_LIST_HEAD(&d->list); | 1262 | INIT_LIST_HEAD(&d->list); |
1003 | list_add(&d->list, &intc_list); | 1263 | list_add_tail(&d->list, &intc_list); |
1264 | |||
1265 | spin_lock_init(&d->lock); | ||
1266 | |||
1267 | d->index = nr_intc_controllers; | ||
1004 | 1268 | ||
1005 | if (desc->num_resources) { | 1269 | if (desc->num_resources) { |
1006 | d->nr_windows = desc->num_resources; | 1270 | d->nr_windows = desc->num_resources; |
@@ -1029,6 +1293,7 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1029 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; | 1293 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; |
1030 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; | 1294 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; |
1031 | d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; | 1295 | d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; |
1296 | d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; | ||
1032 | 1297 | ||
1033 | d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); | 1298 | d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); |
1034 | if (!d->reg) | 1299 | if (!d->reg) |
@@ -1075,6 +1340,11 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1075 | k += save_reg(d, k, hw->sense_regs[i].reg, 0); | 1340 | k += save_reg(d, k, hw->sense_regs[i].reg, 0); |
1076 | } | 1341 | } |
1077 | 1342 | ||
1343 | if (hw->subgroups) | ||
1344 | for (i = 0; i < hw->nr_subgroups; i++) | ||
1345 | if (hw->subgroups[i].reg) | ||
1346 | k+= save_reg(d, k, hw->subgroups[i].reg, 0); | ||
1347 | |||
1078 | d->chip.name = desc->name; | 1348 | d->chip.name = desc->name; |
1079 | d->chip.mask = intc_disable; | 1349 | d->chip.mask = intc_disable; |
1080 | d->chip.unmask = intc_enable; | 1350 | d->chip.unmask = intc_enable; |
@@ -1109,6 +1379,7 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1109 | for (i = 0; i < hw->nr_vectors; i++) { | 1379 | for (i = 0; i < hw->nr_vectors; i++) { |
1110 | struct intc_vect *vect = hw->vectors + i; | 1380 | struct intc_vect *vect = hw->vectors + i; |
1111 | unsigned int irq = evt2irq(vect->vect); | 1381 | unsigned int irq = evt2irq(vect->vect); |
1382 | unsigned long flags; | ||
1112 | struct irq_desc *irq_desc; | 1383 | struct irq_desc *irq_desc; |
1113 | 1384 | ||
1114 | if (!vect->enum_id) | 1385 | if (!vect->enum_id) |
@@ -1120,8 +1391,10 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1120 | continue; | 1391 | continue; |
1121 | } | 1392 | } |
1122 | 1393 | ||
1394 | spin_lock_irqsave(&xlate_lock, flags); | ||
1123 | intc_irq_xlate[irq].enum_id = vect->enum_id; | 1395 | intc_irq_xlate[irq].enum_id = vect->enum_id; |
1124 | intc_irq_xlate[irq].desc = d; | 1396 | intc_irq_xlate[irq].desc = d; |
1397 | spin_unlock_irqrestore(&xlate_lock, flags); | ||
1125 | 1398 | ||
1126 | intc_register_irq(desc, d, vect->enum_id, irq); | 1399 | intc_register_irq(desc, d, vect->enum_id, irq); |
1127 | 1400 | ||
@@ -1152,10 +1425,14 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1152 | } | 1425 | } |
1153 | } | 1426 | } |
1154 | 1427 | ||
1428 | intc_subgroup_init(desc, d); | ||
1429 | |||
1155 | /* enable bits matching force_enable after registering irqs */ | 1430 | /* enable bits matching force_enable after registering irqs */ |
1156 | if (desc->force_enable) | 1431 | if (desc->force_enable) |
1157 | intc_enable_disable_enum(desc, d, desc->force_enable, 1); | 1432 | intc_enable_disable_enum(desc, d, desc->force_enable, 1); |
1158 | 1433 | ||
1434 | nr_intc_controllers++; | ||
1435 | |||
1159 | return 0; | 1436 | return 0; |
1160 | err5: | 1437 | err5: |
1161 | kfree(d->prio); | 1438 | kfree(d->prio); |
@@ -1353,7 +1630,6 @@ static int __init register_intc_sysdevs(void) | |||
1353 | { | 1630 | { |
1354 | struct intc_desc_int *d; | 1631 | struct intc_desc_int *d; |
1355 | int error; | 1632 | int error; |
1356 | int id = 0; | ||
1357 | 1633 | ||
1358 | error = sysdev_class_register(&intc_sysdev_class); | 1634 | error = sysdev_class_register(&intc_sysdev_class); |
1359 | #ifdef CONFIG_INTC_USERIMASK | 1635 | #ifdef CONFIG_INTC_USERIMASK |
@@ -1363,7 +1639,7 @@ static int __init register_intc_sysdevs(void) | |||
1363 | #endif | 1639 | #endif |
1364 | if (!error) { | 1640 | if (!error) { |
1365 | list_for_each_entry(d, &intc_list, list) { | 1641 | list_for_each_entry(d, &intc_list, list) { |
1366 | d->sysdev.id = id; | 1642 | d->sysdev.id = d->index; |
1367 | d->sysdev.cls = &intc_sysdev_class; | 1643 | d->sysdev.cls = &intc_sysdev_class; |
1368 | error = sysdev_register(&d->sysdev); | 1644 | error = sysdev_register(&d->sysdev); |
1369 | if (error == 0) | 1645 | if (error == 0) |
@@ -1371,8 +1647,6 @@ static int __init register_intc_sysdevs(void) | |||
1371 | &attr_name); | 1647 | &attr_name); |
1372 | if (error) | 1648 | if (error) |
1373 | break; | 1649 | break; |
1374 | |||
1375 | id++; | ||
1376 | } | 1650 | } |
1377 | } | 1651 | } |
1378 | 1652 | ||
@@ -1422,9 +1696,7 @@ out_unlock: | |||
1422 | 1696 | ||
1423 | if (irq > 0) { | 1697 | if (irq > 0) { |
1424 | dynamic_irq_init(irq); | 1698 | dynamic_irq_init(irq); |
1425 | #ifdef CONFIG_ARM | 1699 | activate_irq(irq); |
1426 | set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ | ||
1427 | #endif | ||
1428 | } | 1700 | } |
1429 | 1701 | ||
1430 | return irq; | 1702 | return irq; |
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index d40fd77fa75c..04134a6c7b52 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h | |||
@@ -20,6 +20,12 @@ struct intc_group { | |||
20 | 20 | ||
21 | #define INTC_GROUP(enum_id, ids...) { enum_id, { ids } } | 21 | #define INTC_GROUP(enum_id, ids...) { enum_id, { ids } } |
22 | 22 | ||
23 | struct intc_subgroup { | ||
24 | unsigned long reg, reg_width; | ||
25 | intc_enum parent_id; | ||
26 | intc_enum enum_ids[32]; | ||
27 | }; | ||
28 | |||
23 | struct intc_mask_reg { | 29 | struct intc_mask_reg { |
24 | unsigned long set_reg, clr_reg, reg_width; | 30 | unsigned long set_reg, clr_reg, reg_width; |
25 | intc_enum enum_ids[32]; | 31 | intc_enum enum_ids[32]; |
@@ -69,9 +75,12 @@ struct intc_hw_desc { | |||
69 | unsigned int nr_sense_regs; | 75 | unsigned int nr_sense_regs; |
70 | struct intc_mask_reg *ack_regs; | 76 | struct intc_mask_reg *ack_regs; |
71 | unsigned int nr_ack_regs; | 77 | unsigned int nr_ack_regs; |
78 | struct intc_subgroup *subgroups; | ||
79 | unsigned int nr_subgroups; | ||
72 | }; | 80 | }; |
73 | 81 | ||
74 | #define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a) | 82 | #define _INTC_ARRAY(a) a, a == NULL ? 0 : sizeof(a)/sizeof(*a) |
83 | |||
75 | #define INTC_HW_DESC(vectors, groups, mask_regs, \ | 84 | #define INTC_HW_DESC(vectors, groups, mask_regs, \ |
76 | prio_regs, sense_regs, ack_regs) \ | 85 | prio_regs, sense_regs, ack_regs) \ |
77 | { \ | 86 | { \ |
@@ -109,6 +118,7 @@ int __init register_intc_controller(struct intc_desc *desc); | |||
109 | void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs); | 118 | void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs); |
110 | int intc_set_priority(unsigned int irq, unsigned int prio); | 119 | int intc_set_priority(unsigned int irq, unsigned int prio); |
111 | unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id); | 120 | unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id); |
121 | void intc_finalize(void); | ||
112 | 122 | ||
113 | #ifdef CONFIG_INTC_USERIMASK | 123 | #ifdef CONFIG_INTC_USERIMASK |
114 | int register_intc_userimask(unsigned long addr); | 124 | int register_intc_userimask(unsigned long addr); |