Commit c1e30ad98fe210688edca872686db4a715c2fb23

Authored by Paul Mundt
1 parent 44629f57ac

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>

Showing 3 changed files with 351 additions and 67 deletions Side-by-side Diff

arch/sh/kernel/irq.c
... ... @@ -283,6 +283,8 @@
283 283 if (sh_mv.mv_init_irq)
284 284 sh_mv.mv_init_irq();
285 285  
  286 + intc_finalize();
  287 +
286 288 irq_ctx_init(smp_processor_id());
287 289 }
288 290  
... ... @@ -35,6 +35,7 @@
35 35 #include <linux/seq_file.h>
36 36 #include <linux/radix-tree.h>
37 37 #include <linux/mutex.h>
  38 +#include <linux/rcupdate.h>
38 39 #include <asm/sizes.h>
39 40  
40 41 #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
41 42  
... ... @@ -64,11 +65,19 @@
64 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 74 struct intc_desc_int {
68 75 struct list_head list;
69 76 struct sys_device sysdev;
70 77 struct radix_tree_root tree;
71 78 pm_message_t state;
  79 + spinlock_t lock;
  80 + unsigned int index;
72 81 unsigned long *reg;
73 82 #ifdef CONFIG_SMP
74 83 unsigned long *smp;
... ... @@ -84,6 +93,7 @@
84 93 };
85 94  
86 95 static LIST_HEAD(intc_list);
  96 +static unsigned int nr_intc_controllers;
87 97  
88 98 /*
89 99 * The intc_irq_map provides a global map of bound IRQ vectors for a
... ... @@ -99,7 +109,7 @@
99 109 static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
100 110 static struct intc_map_entry intc_irq_xlate[NR_IRQS];
101 111 static DEFINE_SPINLOCK(vector_lock);
102   -static DEFINE_MUTEX(irq_xlate_mutex);
  112 +static DEFINE_SPINLOCK(xlate_lock);
103 113  
104 114 #ifdef CONFIG_SMP
105 115 #define IS_SMP(x) x.smp
106 116  
107 117  
... ... @@ -118,12 +128,39 @@
118 128 static unsigned long dist_handle[NR_IRQS];
119 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 139 static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
122 140 {
123 141 struct irq_chip *chip = get_irq_chip(irq);
  142 +
124 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 164 static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
128 165 unsigned long address)
129 166 {
130 167  
131 168  
132 169  
133 170  
134 171  
135 172  
136 173  
137 174  
138 175  
139 176  
140 177  
141 178  
142 179  
143 180  
... ... @@ -177,56 +214,103 @@
177 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)
181 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)
  246 +{
182 247 __raw_writeb(set_field(0, data, h), addr);
183 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 255 __raw_writew(set_field(0, data, h), addr);
189 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 263 __raw_writel(set_field(0, data, h), addr);
195 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 271 unsigned long flags;
201 272 local_irq_save(flags);
202 273 __raw_writeb(set_field(__raw_readb(addr), data, h), addr);
203 274 (void)__raw_readb(addr); /* Defeat write posting */
204 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 282 unsigned long flags;
210 283 local_irq_save(flags);
211 284 __raw_writew(set_field(__raw_readw(addr), data, h), addr);
212 285 (void)__raw_readw(addr); /* Defeat write posting */
213 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 293 unsigned long flags;
219 294 local_irq_save(flags);
220 295 __raw_writel(set_field(__raw_readl(addr), data, h), addr);
221 296 (void)__raw_readl(addr); /* Defeat write posting */
222 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,
228   - unsigned long h,
229   - unsigned long data) = {
  308 +static unsigned long (*intc_reg_fns[])(unsigned long addr,
  309 + unsigned long h,
  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 314 [REG_FN_WRITE_BASE + 0] = write_8,
231 315 [REG_FN_WRITE_BASE + 1] = write_16,
232 316 [REG_FN_WRITE_BASE + 3] = write_32,
233 317  
234 318  
235 319  
236 320  
237 321  
238 322  
... ... @@ -242,42 +326,42 @@
242 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,
246   - unsigned long handle,
247   - void (*fn)(unsigned long,
248   - unsigned long,
249   - unsigned long),
250   - unsigned int irq)
  329 +static unsigned long intc_mode_field(unsigned long addr,
  330 + unsigned long handle,
  331 + unsigned long (*fn)(unsigned long,
  332 + unsigned long,
  333 + unsigned long),
  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,
256   - unsigned long handle,
257   - void (*fn)(unsigned long,
258   - unsigned long,
259   - unsigned long),
260   - unsigned int irq)
  339 +static unsigned long intc_mode_zero(unsigned long addr,
  340 + unsigned long handle,
  341 + unsigned long (*fn)(unsigned long,
  342 + unsigned long,
  343 + unsigned long),
  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,
266   - unsigned long handle,
267   - void (*fn)(unsigned long,
268   - unsigned long,
269   - unsigned long),
270   - unsigned int irq)
  349 +static unsigned long intc_mode_prio(unsigned long addr,
  350 + unsigned long handle,
  351 + unsigned long (*fn)(unsigned long,
  352 + unsigned long,
  353 + unsigned long),
  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,
276   - unsigned long handle,
277   - void (*fn)(unsigned long,
278   - unsigned long,
279   - unsigned long),
280   - unsigned int irq) = {
  359 +static unsigned long (*intc_enable_fns[])(unsigned long addr,
  360 + unsigned long handle,
  361 + unsigned long (*fn)(unsigned long,
  362 + unsigned long,
  363 + unsigned long),
  364 + unsigned int irq) = {
281 365 [MODE_ENABLE_REG] = intc_mode_field,
282 366 [MODE_MASK_REG] = intc_mode_zero,
283 367 [MODE_DUAL_REG] = intc_mode_field,
284 368  
... ... @@ -285,9 +369,9 @@
285 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 373 unsigned long handle,
290   - void (*fn)(unsigned long,
  374 + unsigned long (*fn)(unsigned long,
291 375 unsigned long,
292 376 unsigned long),
293 377 unsigned int irq) = {
... ... @@ -421,12 +505,13 @@
421 505 }
422 506 }
423 507  
424   -static void (*intc_enable_noprio_fns[])(unsigned long addr,
425   - unsigned long handle,
426   - void (*fn)(unsigned long,
427   - unsigned long,
428   - unsigned long),
429   - unsigned int irq) = {
  508 +static unsigned long
  509 +(*intc_enable_noprio_fns[])(unsigned long addr,
  510 + unsigned long handle,
  511 + unsigned long (*fn)(unsigned long,
  512 + unsigned long,
  513 + unsigned long),
  514 + unsigned int irq) = {
430 515 [MODE_ENABLE_REG] = intc_mode_field,
431 516 [MODE_MASK_REG] = intc_mode_zero,
432 517 [MODE_DUAL_REG] = intc_mode_field,
... ... @@ -439,8 +524,9 @@
439 524 {
440 525 unsigned long addr;
441 526 unsigned int cpu;
442   - void (*fn)(unsigned long, unsigned long,
443   - void (*)(unsigned long, unsigned long, unsigned long),
  527 + unsigned long (*fn)(unsigned long, unsigned long,
  528 + unsigned long (*)(unsigned long, unsigned long,
  529 + unsigned long),
444 530 unsigned int);
445 531  
446 532 if (do_enable) {
... ... @@ -861,6 +947,186 @@
861 947 }
862 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 1130 static void __init intc_register_irq(struct intc_desc *desc,
865 1131 struct intc_desc_int *d,
866 1132 intc_enum enum_id,
... ... @@ -868,6 +1134,7 @@
868 1134 {
869 1135 struct intc_handle_int *hp;
870 1136 unsigned int data[2], primary;
  1137 + unsigned long flags;
871 1138  
872 1139 /*
873 1140 * Register the IRQ position with the global IRQ map, then insert
874 1141  
... ... @@ -875,9 +1142,9 @@
875 1142 */
876 1143 set_bit(irq, intc_irq_map);
877 1144  
878   - mutex_lock(&irq_xlate_mutex);
  1145 + spin_lock_irqsave(&xlate_lock, flags);
879 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 1150 * Prefer single interrupt source bitmap over other combinations:
... ... @@ -957,9 +1224,7 @@
957 1224 dist_handle[irq] = intc_dist_data(desc, d, enum_id);
958 1225 #endif
959 1226  
960   -#ifdef CONFIG_ARM
961   - set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
962   -#endif
  1227 + activate_irq(irq);
963 1228 }
964 1229  
965 1230 static unsigned int __init save_reg(struct intc_desc_int *d,
... ... @@ -980,11 +1245,6 @@
980 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 1248 int __init register_intc_controller(struct intc_desc *desc)
989 1249 {
990 1250 unsigned int i, k, smp;
991 1251  
... ... @@ -1000,8 +1260,12 @@
1000 1260 goto err0;
1001 1261  
1002 1262 INIT_LIST_HEAD(&d->list);
1003   - list_add(&d->list, &intc_list);
  1263 + list_add_tail(&d->list, &intc_list);
1004 1264  
  1265 + spin_lock_init(&d->lock);
  1266 +
  1267 + d->index = nr_intc_controllers;
  1268 +
1005 1269 if (desc->num_resources) {
1006 1270 d->nr_windows = desc->num_resources;
1007 1271 d->window = kzalloc(d->nr_windows * sizeof(*d->window),
... ... @@ -1029,6 +1293,7 @@
1029 1293 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
1030 1294 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
1031 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 1298 d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
1034 1299 if (!d->reg)
... ... @@ -1075,6 +1340,11 @@
1075 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 1348 d->chip.name = desc->name;
1079 1349 d->chip.mask = intc_disable;
1080 1350 d->chip.unmask = intc_enable;
... ... @@ -1109,6 +1379,7 @@
1109 1379 for (i = 0; i < hw->nr_vectors; i++) {
1110 1380 struct intc_vect *vect = hw->vectors + i;
1111 1381 unsigned int irq = evt2irq(vect->vect);
  1382 + unsigned long flags;
1112 1383 struct irq_desc *irq_desc;
1113 1384  
1114 1385 if (!vect->enum_id)
1115 1386  
... ... @@ -1120,8 +1391,10 @@
1120 1391 continue;
1121 1392 }
1122 1393  
  1394 + spin_lock_irqsave(&xlate_lock, flags);
1123 1395 intc_irq_xlate[irq].enum_id = vect->enum_id;
1124 1396 intc_irq_xlate[irq].desc = d;
  1397 + spin_unlock_irqrestore(&xlate_lock, flags);
1125 1398  
1126 1399 intc_register_irq(desc, d, vect->enum_id, irq);
1127 1400  
1128 1401  
... ... @@ -1152,10 +1425,14 @@
1152 1425 }
1153 1426 }
1154 1427  
  1428 + intc_subgroup_init(desc, d);
  1429 +
1155 1430 /* enable bits matching force_enable after registering irqs */
1156 1431 if (desc->force_enable)
1157 1432 intc_enable_disable_enum(desc, d, desc->force_enable, 1);
1158 1433  
  1434 + nr_intc_controllers++;
  1435 +
1159 1436 return 0;
1160 1437 err5:
1161 1438 kfree(d->prio);
... ... @@ -1353,7 +1630,6 @@
1353 1630 {
1354 1631 struct intc_desc_int *d;
1355 1632 int error;
1356   - int id = 0;
1357 1633  
1358 1634 error = sysdev_class_register(&intc_sysdev_class);
1359 1635 #ifdef CONFIG_INTC_USERIMASK
... ... @@ -1363,7 +1639,7 @@
1363 1639 #endif
1364 1640 if (!error) {
1365 1641 list_for_each_entry(d, &intc_list, list) {
1366   - d->sysdev.id = id;
  1642 + d->sysdev.id = d->index;
1367 1643 d->sysdev.cls = &intc_sysdev_class;
1368 1644 error = sysdev_register(&d->sysdev);
1369 1645 if (error == 0)
... ... @@ -1371,8 +1647,6 @@
1371 1647 &attr_name);
1372 1648 if (error)
1373 1649 break;
1374   -
1375   - id++;
1376 1650 }
1377 1651 }
1378 1652  
... ... @@ -1422,9 +1696,7 @@
1422 1696  
1423 1697 if (irq > 0) {
1424 1698 dynamic_irq_init(irq);
1425   -#ifdef CONFIG_ARM
1426   - set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
1427   -#endif
  1699 + activate_irq(irq);
1428 1700 }
1429 1701  
1430 1702 return irq;
include/linux/sh_intc.h
... ... @@ -20,6 +20,12 @@
20 20  
21 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 29 struct intc_mask_reg {
24 30 unsigned long set_reg, clr_reg, reg_width;
25 31 intc_enum enum_ids[32];
26 32  
... ... @@ -69,9 +75,12 @@
69 75 unsigned int nr_sense_regs;
70 76 struct intc_mask_reg *ack_regs;
71 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 84 #define INTC_HW_DESC(vectors, groups, mask_regs, \
76 85 prio_regs, sense_regs, ack_regs) \
77 86 { \
... ... @@ -109,6 +118,7 @@
109 118 void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs);
110 119 int intc_set_priority(unsigned int irq, unsigned int prio);
111 120 unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id);
  121 +void intc_finalize(void);
112 122  
113 123 #ifdef CONFIG_INTC_USERIMASK
114 124 int register_intc_userimask(unsigned long addr);