aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-10-04 15:47:03 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-10-04 15:47:03 -0400
commitc1e30ad98fe210688edca872686db4a715c2fb23 (patch)
tree1ff151ccc9658d7a2d89da9cfc6a2d6817913a79
parent44629f57accccbb8e6d443246fe6f51b42f7f781 (diff)
sh: intc: Support virtual mappings for IRQ subgroups.
Many interrupts that share a single mask source but are on different hardware vectors will have an associated register tied to an INTEVT that denotes the precise cause for the interrupt exception being triggered. This introduces the concept of IRQ subgroups in the intc core, where a virtual IRQ map is constructed for each of the pre-defined cause bits, and a higher level chained handler takes control of the parent INTEVT. This enables CPUs with heavily muxed IRQ vectors (especially across disjoint blocks) to break things out in to a series of managed chained handlers while being able to dynamically lookup and adopt the IRQs created for them. This is largely an opt-in interface, requiring CPUs to manually submit IRQs for subgroup splitting, in addition to providing identifiers in their enum maps that can be used for lazy lookup via the radix tree. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--arch/sh/kernel/irq.c2
-rw-r--r--drivers/sh/intc.c404
-rw-r--r--include/linux/sh_intc.h12
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
68struct intc_subgroup_entry {
69 unsigned int pirq;
70 intc_enum enum_id;
71 unsigned long handle;
72};
73
67struct intc_desc_int { 74struct 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
86static LIST_HEAD(intc_list); 95static LIST_HEAD(intc_list);
96static 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);
99static DECLARE_BITMAP(intc_irq_map, NR_IRQS); 109static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
100static struct intc_map_entry intc_irq_xlate[NR_IRQS]; 110static struct intc_map_entry intc_irq_xlate[NR_IRQS];
101static DEFINE_SPINLOCK(vector_lock); 111static DEFINE_SPINLOCK(vector_lock);
102static DEFINE_MUTEX(irq_xlate_mutex); 112static 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];
118static unsigned long dist_handle[NR_IRQS]; 128static unsigned long dist_handle[NR_IRQS];
119#endif 129#endif
120 130
131struct 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
121static inline struct intc_desc_int *get_intc_desc(unsigned int irq) 139static 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
146static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
147{
148 generic_handle_irq((unsigned int)get_irq_data(irq));
149}
150
151static 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
127static unsigned long intc_phys_to_virt(struct intc_desc_int *d, 164static 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
180static void write_8(unsigned long addr, unsigned long h, unsigned long data) 217static 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
226static 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
232static 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
238static 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
244static 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
186static void write_16(unsigned long addr, unsigned long h, unsigned long data) 252static 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
192static void write_32(unsigned long addr, unsigned long h, unsigned long data) 260static 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
198static void modify_8(unsigned long addr, unsigned long h, unsigned long data) 268static 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
207static void modify_16(unsigned long addr, unsigned long h, unsigned long data) 279static 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
216static void modify_32(unsigned long addr, unsigned long h, unsigned long data) 290static 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
225enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; 301enum {
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
227static void (*intc_reg_fns[])(unsigned long addr, 308static 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
245static void intc_mode_field(unsigned long addr, 329static 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
255static void intc_mode_zero(unsigned long addr, 339static 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
265static void intc_mode_prio(unsigned long addr, 349static 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
275static void (*intc_enable_fns[])(unsigned long addr, 359static 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
288static void (*intc_disable_fns[])(unsigned long addr, 372static 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
424static void (*intc_enable_noprio_fns[])(unsigned long addr, 508static 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}
862EXPORT_SYMBOL_GPL(intc_irq_lookup); 948EXPORT_SYMBOL_GPL(intc_irq_lookup);
863 949
950static 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
976static 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
996static 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
1008static 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
1053static 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
1065static 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
1074restart:
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
1121void __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
864static void __init intc_register_irq(struct intc_desc *desc, 1130static 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
965static unsigned int __init save_reg(struct intc_desc_int *d, 1230static 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
983static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
984{
985 generic_handle_irq((unsigned int)get_irq_data(irq));
986}
987
988int __init register_intc_controller(struct intc_desc *desc) 1248int __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;
1160err5: 1437err5:
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
23struct intc_subgroup {
24 unsigned long reg, reg_width;
25 intc_enum parent_id;
26 intc_enum enum_ids[32];
27};
28
23struct intc_mask_reg { 29struct 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);
109void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs); 118void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs);
110int intc_set_priority(unsigned int irq, unsigned int prio); 119int intc_set_priority(unsigned int irq, unsigned int prio);
111unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id); 120unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id);
121void intc_finalize(void);
112 122
113#ifdef CONFIG_INTC_USERIMASK 123#ifdef CONFIG_INTC_USERIMASK
114int register_intc_userimask(unsigned long addr); 124int register_intc_userimask(unsigned long addr);