diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index 257de1f0692b19ade90daefcf1089c3c127ce72c..c8e1409f20a2157810972ef48e20f81eb9e87e19 100644
--- a/arch/sh/kernel/irq.c
+++ b/arch/sh/kernel/irq.c
@@ -283,6 +283,8 @@ void __init init_IRQ(void)
 	if (sh_mv.mv_init_irq)
 		sh_mv.mv_init_irq();
 
+	intc_finalize();
+
 	irq_ctx_init(smp_processor_id());
 }
 
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index a27dcb4254c7f529a7b16cfe9141a301ba7650f6..c81fe23db7f74a6cc4cc17f174e375b34912eac4 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -35,6 +35,7 @@
 #include <linux/seq_file.h>
 #include <linux/radix-tree.h>
 #include <linux/mutex.h>
+#include <linux/rcupdate.h>
 #include <asm/sizes.h>
 
 #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
@@ -64,11 +65,19 @@ struct intc_map_entry {
 	struct intc_desc_int *desc;
 };
 
+struct intc_subgroup_entry {
+	unsigned int pirq;
+	intc_enum enum_id;
+	unsigned long handle;
+};
+
 struct intc_desc_int {
 	struct list_head list;
 	struct sys_device sysdev;
 	struct radix_tree_root tree;
 	pm_message_t state;
+	spinlock_t lock;
+	unsigned int index;
 	unsigned long *reg;
 #ifdef CONFIG_SMP
 	unsigned long *smp;
@@ -84,6 +93,7 @@ struct intc_desc_int {
 };
 
 static LIST_HEAD(intc_list);
+static unsigned int nr_intc_controllers;
 
 /*
  * The intc_irq_map provides a global map of bound IRQ vectors for a
@@ -99,7 +109,7 @@ static LIST_HEAD(intc_list);
 static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
 static struct intc_map_entry intc_irq_xlate[NR_IRQS];
 static DEFINE_SPINLOCK(vector_lock);
-static DEFINE_MUTEX(irq_xlate_mutex);
+static DEFINE_SPINLOCK(xlate_lock);
 
 #ifdef CONFIG_SMP
 #define IS_SMP(x) x.smp
@@ -118,12 +128,39 @@ static unsigned long ack_handle[NR_IRQS];
 static unsigned long dist_handle[NR_IRQS];
 #endif
 
+struct intc_virq_list {
+	unsigned int irq;
+	struct intc_virq_list *next;
+};
+
+#define for_each_virq(entry, head) \
+	for (entry = head; entry; entry = entry->next)
+
 static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
 {
 	struct irq_chip *chip = get_irq_chip(irq);
+
 	return container_of(chip, struct intc_desc_int, chip);
 }
 
+static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
+{
+	generic_handle_irq((unsigned int)get_irq_data(irq));
+}
+
+static inline void activate_irq(int irq)
+{
+#ifdef CONFIG_ARM
+	/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
+	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
+	 */
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	/* same effect on other architectures */
+	set_irq_noprobe(irq);
+#endif
+}
+
 static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
 				       unsigned long address)
 {
@@ -177,56 +214,103 @@ static inline unsigned int set_field(unsigned int value,
 	return value;
 }
 
-static void write_8(unsigned long addr, unsigned long h, unsigned long data)
+static inline unsigned long get_field(unsigned int value, unsigned int handle)
+{
+	unsigned int width = _INTC_WIDTH(handle);
+	unsigned int shift = _INTC_SHIFT(handle);
+	unsigned int mask = ((1 << width) - 1) << shift;
+
+	return (value & mask) >> shift;
+}
+
+static unsigned long test_8(unsigned long addr, unsigned long h,
+			    unsigned long ignore)
+{
+	return get_field(__raw_readb(addr), h);
+}
+
+static unsigned long test_16(unsigned long addr, unsigned long h,
+			     unsigned long ignore)
+{
+	return get_field(__raw_readw(addr), h);
+}
+
+static unsigned long test_32(unsigned long addr, unsigned long h,
+			     unsigned long ignore)
+{
+	return get_field(__raw_readl(addr), h);
+}
+
+static unsigned long write_8(unsigned long addr, unsigned long h,
+			     unsigned long data)
 {
 	__raw_writeb(set_field(0, data, h), addr);
 	(void)__raw_readb(addr);	/* Defeat write posting */
+	return 0;
 }
 
-static void write_16(unsigned long addr, unsigned long h, unsigned long data)
+static unsigned long write_16(unsigned long addr, unsigned long h,
+			      unsigned long data)
 {
 	__raw_writew(set_field(0, data, h), addr);
 	(void)__raw_readw(addr);	/* Defeat write posting */
+	return 0;
 }
 
-static void write_32(unsigned long addr, unsigned long h, unsigned long data)
+static unsigned long write_32(unsigned long addr, unsigned long h,
+			      unsigned long data)
 {
 	__raw_writel(set_field(0, data, h), addr);
 	(void)__raw_readl(addr);	/* Defeat write posting */
+	return 0;
 }
 
-static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
+static unsigned long modify_8(unsigned long addr, unsigned long h,
+			      unsigned long data)
 {
 	unsigned long flags;
 	local_irq_save(flags);
 	__raw_writeb(set_field(__raw_readb(addr), data, h), addr);
 	(void)__raw_readb(addr);	/* Defeat write posting */
 	local_irq_restore(flags);
+	return 0;
 }
 
-static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
+static unsigned long modify_16(unsigned long addr, unsigned long h,
+			       unsigned long data)
 {
 	unsigned long flags;
 	local_irq_save(flags);
 	__raw_writew(set_field(__raw_readw(addr), data, h), addr);
 	(void)__raw_readw(addr);	/* Defeat write posting */
 	local_irq_restore(flags);
+	return 0;
 }
 
-static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
+static unsigned long modify_32(unsigned long addr, unsigned long h,
+			       unsigned long data)
 {
 	unsigned long flags;
 	local_irq_save(flags);
 	__raw_writel(set_field(__raw_readl(addr), data, h), addr);
 	(void)__raw_readl(addr);	/* Defeat write posting */
 	local_irq_restore(flags);
+	return 0;
 }
 
-enum {	REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 };
+enum {
+	REG_FN_ERR = 0,
+	REG_FN_TEST_BASE = 1,
+	REG_FN_WRITE_BASE = 5,
+	REG_FN_MODIFY_BASE = 9
+};
 
-static void (*intc_reg_fns[])(unsigned long addr,
-			      unsigned long h,
-			      unsigned long data) = {
+static unsigned long (*intc_reg_fns[])(unsigned long addr,
+				       unsigned long h,
+				       unsigned long data) = {
+	[REG_FN_TEST_BASE + 0] = test_8,
+	[REG_FN_TEST_BASE + 1] = test_16,
+	[REG_FN_TEST_BASE + 3] = test_32,
 	[REG_FN_WRITE_BASE + 0] = write_8,
 	[REG_FN_WRITE_BASE + 1] = write_16,
 	[REG_FN_WRITE_BASE + 3] = write_32,
@@ -242,42 +326,42 @@ enum {	MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */
 	MODE_PCLR_REG,       /* Above plus all bits set to disable interrupt */
 };
 
-static void intc_mode_field(unsigned long addr,
-			    unsigned long handle,
-			    void (*fn)(unsigned long,
-				       unsigned long,
-				       unsigned long),
-			    unsigned int irq)
+static unsigned long intc_mode_field(unsigned long addr,
+				     unsigned long handle,
+				     unsigned long (*fn)(unsigned long,
+						unsigned long,
+						unsigned long),
+				     unsigned int irq)
 {
-	fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
+	return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
 }
 
-static void intc_mode_zero(unsigned long addr,
-			   unsigned long handle,
-			   void (*fn)(unsigned long,
-				       unsigned long,
-				       unsigned long),
-			   unsigned int irq)
+static unsigned long intc_mode_zero(unsigned long addr,
+				    unsigned long handle,
+				    unsigned long (*fn)(unsigned long,
+					       unsigned long,
+					       unsigned long),
+				    unsigned int irq)
 {
-	fn(addr, handle, 0);
+	return fn(addr, handle, 0);
 }
 
-static void intc_mode_prio(unsigned long addr,
-			   unsigned long handle,
-			   void (*fn)(unsigned long,
-				       unsigned long,
-				       unsigned long),
-			   unsigned int irq)
+static unsigned long intc_mode_prio(unsigned long addr,
+				    unsigned long handle,
+				    unsigned long (*fn)(unsigned long,
+					       unsigned long,
+					       unsigned long),
+				    unsigned int irq)
 {
-	fn(addr, handle, intc_prio_level[irq]);
+	return fn(addr, handle, intc_prio_level[irq]);
 }
 
-static void (*intc_enable_fns[])(unsigned long addr,
-				 unsigned long handle,
-				 void (*fn)(unsigned long,
-					    unsigned long,
-					    unsigned long),
-				 unsigned int irq) = {
+static unsigned long (*intc_enable_fns[])(unsigned long addr,
+					  unsigned long handle,
+					  unsigned long (*fn)(unsigned long,
+						    unsigned long,
+						    unsigned long),
+					  unsigned int irq) = {
 	[MODE_ENABLE_REG] = intc_mode_field,
 	[MODE_MASK_REG] = intc_mode_zero,
 	[MODE_DUAL_REG] = intc_mode_field,
@@ -285,9 +369,9 @@ static void (*intc_enable_fns[])(unsigned long addr,
 	[MODE_PCLR_REG] = intc_mode_prio,
 };
 
-static void (*intc_disable_fns[])(unsigned long addr,
+static unsigned long (*intc_disable_fns[])(unsigned long addr,
 				  unsigned long handle,
-				  void (*fn)(unsigned long,
+				  unsigned long (*fn)(unsigned long,
 					     unsigned long,
 					     unsigned long),
 				  unsigned int irq) = {
@@ -421,12 +505,13 @@ static void intc_disable(unsigned int irq)
 	}
 }
 
-static void (*intc_enable_noprio_fns[])(unsigned long addr,
-					unsigned long handle,
-					void (*fn)(unsigned long,
-						   unsigned long,
-						   unsigned long),
-					unsigned int irq) = {
+static unsigned long
+(*intc_enable_noprio_fns[])(unsigned long addr,
+			    unsigned long handle,
+			    unsigned long (*fn)(unsigned long,
+					unsigned long,
+					unsigned long),
+			    unsigned int irq) = {
 	[MODE_ENABLE_REG] = intc_mode_field,
 	[MODE_MASK_REG] = intc_mode_zero,
 	[MODE_DUAL_REG] = intc_mode_field,
@@ -439,8 +524,9 @@ static void intc_enable_disable(struct intc_desc_int *d,
 {
 	unsigned long addr;
 	unsigned int cpu;
-	void (*fn)(unsigned long, unsigned long,
-		   void (*)(unsigned long, unsigned long, unsigned long),
+	unsigned long (*fn)(unsigned long, unsigned long,
+		   unsigned long (*)(unsigned long, unsigned long,
+				     unsigned long),
 		   unsigned int);
 
 	if (do_enable) {
@@ -861,6 +947,186 @@ unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id)
 }
 EXPORT_SYMBOL_GPL(intc_irq_lookup);
 
+static int add_virq_to_pirq(unsigned int irq, unsigned int virq)
+{
+	struct intc_virq_list **last, *entry;
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	/* scan for duplicates */
+	last = (struct intc_virq_list **)&desc->handler_data;
+	for_each_virq(entry, desc->handler_data) {
+		if (entry->irq == virq)
+			return 0;
+		last = &entry->next;
+	}
+
+	entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC);
+	if (!entry) {
+		pr_err("can't allocate VIRQ mapping for %d\n", virq);
+		return -ENOMEM;
+	}
+
+	entry->irq = virq;
+
+	*last = entry;
+
+	return 0;
+}
+
+static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct intc_virq_list *entry, *vlist = get_irq_data(irq);
+	struct intc_desc_int *d = get_intc_desc(irq);
+
+	desc->chip->mask_ack(irq);
+
+	for_each_virq(entry, vlist) {
+		unsigned long addr, handle;
+
+		handle = (unsigned long)get_irq_data(entry->irq);
+		addr = INTC_REG(d, _INTC_ADDR_E(handle), 0);
+
+		if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0))
+			generic_handle_irq(entry->irq);
+	}
+
+	desc->chip->unmask(irq);
+}
+
+static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup,
+					       struct intc_desc_int *d,
+					       unsigned int index)
+{
+	unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1;
+
+	return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg),
+			0, 1, (subgroup->reg_width - 1) - index);
+}
+
+#define INTC_TAG_VIRQ_NEEDS_ALLOC	0
+
+static void __init intc_subgroup_init_one(struct intc_desc *desc,
+					  struct intc_desc_int *d,
+					  struct intc_subgroup *subgroup)
+{
+	struct intc_map_entry *mapped;
+	unsigned int pirq;
+	unsigned long flags;
+	int i;
+
+	mapped = radix_tree_lookup(&d->tree, subgroup->parent_id);
+	if (!mapped) {
+		WARN_ON(1);
+		return;
+	}
+
+	pirq = mapped - intc_irq_xlate;
+
+	spin_lock_irqsave(&d->lock, flags);
+
+	for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) {
+		struct intc_subgroup_entry *entry;
+		int err;
+
+		if (!subgroup->enum_ids[i])
+			continue;
+
+		entry = kmalloc(sizeof(*entry), GFP_NOWAIT);
+		if (!entry)
+			break;
+
+		entry->pirq = pirq;
+		entry->enum_id = subgroup->enum_ids[i];
+		entry->handle = intc_subgroup_data(subgroup, d, i);
+
+		err = radix_tree_insert(&d->tree, entry->enum_id, entry);
+		if (unlikely(err < 0))
+			break;
+
+		radix_tree_tag_set(&d->tree, entry->enum_id,
+				   INTC_TAG_VIRQ_NEEDS_ALLOC);
+	}
+
+	spin_unlock_irqrestore(&d->lock, flags);
+}
+
+static void __init intc_subgroup_init(struct intc_desc *desc,
+				      struct intc_desc_int *d)
+{
+	int i;
+
+	if (!desc->hw.subgroups)
+		return;
+
+	for (i = 0; i < desc->hw.nr_subgroups; i++)
+		intc_subgroup_init_one(desc, d, desc->hw.subgroups + i);
+}
+
+static void __init intc_subgroup_map(struct intc_desc_int *d)
+{
+	struct intc_subgroup_entry *entries[32];
+	unsigned long flags;
+	unsigned int nr_found;
+	int i;
+
+	spin_lock_irqsave(&d->lock, flags);
+
+restart:
+	nr_found = radix_tree_gang_lookup_tag_slot(&d->tree,
+			(void ***)entries, 0, ARRAY_SIZE(entries),
+			INTC_TAG_VIRQ_NEEDS_ALLOC);
+
+	for (i = 0; i < nr_found; i++) {
+		struct intc_subgroup_entry *entry;
+		int irq;
+
+		entry = radix_tree_deref_slot((void **)entries[i]);
+		if (unlikely(!entry))
+			continue;
+		if (unlikely(entry == RADIX_TREE_RETRY))
+			goto restart;
+
+		irq = create_irq();
+		if (unlikely(irq < 0)) {
+			pr_err("no more free IRQs, bailing..\n");
+			break;
+		}
+
+		pr_info("Setting up a chained VIRQ from %d -> %d\n",
+			irq, entry->pirq);
+
+		spin_lock(&xlate_lock);
+		intc_irq_xlate[irq].desc = d;
+		intc_irq_xlate[irq].enum_id = entry->enum_id;
+		spin_unlock(&xlate_lock);
+
+		set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq),
+					      handle_simple_irq, "virq");
+		set_irq_chip_data(irq, get_irq_chip_data(entry->pirq));
+
+		set_irq_data(irq, (void *)entry->handle);
+
+		set_irq_chained_handler(entry->pirq, intc_virq_handler);
+		add_virq_to_pirq(entry->pirq, irq);
+
+		radix_tree_tag_clear(&d->tree, entry->enum_id,
+				     INTC_TAG_VIRQ_NEEDS_ALLOC);
+		radix_tree_replace_slot((void **)entries[i],
+					&intc_irq_xlate[irq]);
+	}
+
+	spin_unlock_irqrestore(&d->lock, flags);
+}
+
+void __init intc_finalize(void)
+{
+	struct intc_desc_int *d;
+
+	list_for_each_entry(d, &intc_list, list)
+		if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC))
+			intc_subgroup_map(d);
+}
+
 static void __init intc_register_irq(struct intc_desc *desc,
 				     struct intc_desc_int *d,
 				     intc_enum enum_id,
@@ -868,6 +1134,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
 {
 	struct intc_handle_int *hp;
 	unsigned int data[2], primary;
+	unsigned long flags;
 
 	/*
 	 * 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,
 	 */
 	set_bit(irq, intc_irq_map);
 
-	mutex_lock(&irq_xlate_mutex);
+	spin_lock_irqsave(&xlate_lock, flags);
 	radix_tree_insert(&d->tree, enum_id, &intc_irq_xlate[irq]);
-	mutex_unlock(&irq_xlate_mutex);
+	spin_unlock_irqrestore(&xlate_lock, flags);
 
 	/*
 	 * Prefer single interrupt source bitmap over other combinations:
@@ -957,9 +1224,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
 		dist_handle[irq] = intc_dist_data(desc, d, enum_id);
 #endif
 
-#ifdef CONFIG_ARM
-	set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
-#endif
+	activate_irq(irq);
 }
 
 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,
 	return 0;
 }
 
-static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
-{
-	generic_handle_irq((unsigned int)get_irq_data(irq));
-}
-
 int __init register_intc_controller(struct intc_desc *desc)
 {
 	unsigned int i, k, smp;
@@ -1000,7 +1260,11 @@ int __init register_intc_controller(struct intc_desc *desc)
 		goto err0;
 
 	INIT_LIST_HEAD(&d->list);
-	list_add(&d->list, &intc_list);
+	list_add_tail(&d->list, &intc_list);
+
+	spin_lock_init(&d->lock);
+
+	d->index = nr_intc_controllers;
 
 	if (desc->num_resources) {
 		d->nr_windows = desc->num_resources;
@@ -1029,6 +1293,7 @@ int __init register_intc_controller(struct intc_desc *desc)
 	d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
 	d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
 	d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
+	d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0;
 
 	d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
 	if (!d->reg)
@@ -1075,6 +1340,11 @@ int __init register_intc_controller(struct intc_desc *desc)
 			k += save_reg(d, k, hw->sense_regs[i].reg, 0);
 	}
 
+	if (hw->subgroups)
+		for (i = 0; i < hw->nr_subgroups; i++)
+			if (hw->subgroups[i].reg)
+				k+= save_reg(d, k, hw->subgroups[i].reg, 0);
+
 	d->chip.name = desc->name;
 	d->chip.mask = intc_disable;
 	d->chip.unmask = intc_enable;
@@ -1109,6 +1379,7 @@ int __init register_intc_controller(struct intc_desc *desc)
 	for (i = 0; i < hw->nr_vectors; i++) {
 		struct intc_vect *vect = hw->vectors + i;
 		unsigned int irq = evt2irq(vect->vect);
+		unsigned long flags;
 		struct irq_desc *irq_desc;
 
 		if (!vect->enum_id)
@@ -1120,8 +1391,10 @@ int __init register_intc_controller(struct intc_desc *desc)
 			continue;
 		}
 
+		spin_lock_irqsave(&xlate_lock, flags);
 		intc_irq_xlate[irq].enum_id = vect->enum_id;
 		intc_irq_xlate[irq].desc = d;
+		spin_unlock_irqrestore(&xlate_lock, flags);
 
 		intc_register_irq(desc, d, vect->enum_id, irq);
 
@@ -1152,10 +1425,14 @@ int __init register_intc_controller(struct intc_desc *desc)
 		}
 	}
 
+	intc_subgroup_init(desc, d);
+
 	/* enable bits matching force_enable after registering irqs */
 	if (desc->force_enable)
 		intc_enable_disable_enum(desc, d, desc->force_enable, 1);
 
+	nr_intc_controllers++;
+
 	return 0;
 err5:
 	kfree(d->prio);
@@ -1353,7 +1630,6 @@ static int __init register_intc_sysdevs(void)
 {
 	struct intc_desc_int *d;
 	int error;
-	int id = 0;
 
 	error = sysdev_class_register(&intc_sysdev_class);
 #ifdef CONFIG_INTC_USERIMASK
@@ -1363,7 +1639,7 @@ static int __init register_intc_sysdevs(void)
 #endif
 	if (!error) {
 		list_for_each_entry(d, &intc_list, list) {
-			d->sysdev.id = id;
+			d->sysdev.id = d->index;
 			d->sysdev.cls = &intc_sysdev_class;
 			error = sysdev_register(&d->sysdev);
 			if (error == 0)
@@ -1371,8 +1647,6 @@ static int __init register_intc_sysdevs(void)
 							   &attr_name);
 			if (error)
 				break;
-
-			id++;
 		}
 	}
 
@@ -1422,9 +1696,7 @@ out_unlock:
 
 	if (irq > 0) {
 		dynamic_irq_init(irq);
-#ifdef CONFIG_ARM
-		set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
-#endif
+		activate_irq(irq);
 	}
 
 	return irq;
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index d40fd77fa75c9ba8eb5a8b8d7e8ec3d20c84a2de..04134a6c7b52b638c163de9ce72e6275f7410056 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -20,6 +20,12 @@ struct intc_group {
 
 #define INTC_GROUP(enum_id, ids...) { enum_id, { ids } }
 
+struct intc_subgroup {
+	unsigned long reg, reg_width;
+	intc_enum parent_id;
+	intc_enum enum_ids[32];
+};
+
 struct intc_mask_reg {
 	unsigned long set_reg, clr_reg, reg_width;
 	intc_enum enum_ids[32];
@@ -69,9 +75,12 @@ struct intc_hw_desc {
 	unsigned int nr_sense_regs;
 	struct intc_mask_reg *ack_regs;
 	unsigned int nr_ack_regs;
+	struct intc_subgroup *subgroups;
+	unsigned int nr_subgroups;
 };
 
-#define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a)
+#define _INTC_ARRAY(a) a, a == NULL ? 0 : sizeof(a)/sizeof(*a)
+
 #define INTC_HW_DESC(vectors, groups, mask_regs,	\
 		     prio_regs,	sense_regs, ack_regs)	\
 {							\
@@ -109,6 +118,7 @@ int __init register_intc_controller(struct intc_desc *desc);
 void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs);
 int intc_set_priority(unsigned int irq, unsigned int prio);
 unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id);
+void intc_finalize(void);
 
 #ifdef CONFIG_INTC_USERIMASK
 int register_intc_userimask(unsigned long addr);