[SPARC64]: SUN4U PCI-E controller support.

Some minor refactoring in the generic code was necessary for
this:

1) This controller requires 8-byte access to the interrupt map
   and clear register.  They are 64-bits on all the other
   SBUS and PCI controllers anyways, so this was easy to cure.

2) The IMAP register has a different layout and some bits that we
   need to preserve, so use a read/modify/write when making
   changes to the IMAP register in generic code.

3) Flushing the entire IOMMU TLB is best done with a single write
   to a register on this PCI controller, add a iommu->iommu_flushinv
   for this.

Still lacks MSI support, that will come later.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 5e1fcd0..6625ac8 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -386,11 +386,9 @@
 
 	/* Now build the IRQ bucket. */
 	imap = controller_regs + imap_off;
-	imap += 4;
 
 	iclr_off = psycho_iclr_offset(ino);
 	iclr = controller_regs + iclr_off;
-	iclr += 4;
 
 	if ((ino & 0x20) == 0)
 		inofixup = ino & 0x03;
@@ -613,11 +611,9 @@
 
 	/* Now build the IRQ bucket. */
 	imap = controller_regs + imap_off;
-	imap += 4;
 
 	iclr_off = sabre_iclr_offset(ino);
 	iclr = controller_regs + iclr_off;
-	iclr += 4;
 
 	if ((ino & 0x20) == 0)
 		inofixup = ino & 0x03;
@@ -679,13 +675,14 @@
 static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
 					unsigned int ino)
 {
-	return pbm_regs + schizo_iclr_offset(ino) + 4;
+
+	return pbm_regs + schizo_iclr_offset(ino);
 }
 
 static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
 					unsigned int ino)
 {
-	return pbm_regs + schizo_imap_offset(ino) + 4;
+	return pbm_regs + schizo_imap_offset(ino);
 }
 
 #define schizo_read(__reg) \
@@ -848,6 +845,85 @@
 	dp->irq_trans->data = (void *) (unsigned long)
 		((regs->phys_addr >> 32UL) & 0x0fffffff);
 }
+
+struct fire_irq_data {
+	unsigned long pbm_regs;
+	u32 portid;
+};
+
+#define FIRE_IMAP_BASE	0x001000
+#define FIRE_ICLR_BASE	0x001400
+
+static unsigned long fire_imap_offset(unsigned long ino)
+{
+	return FIRE_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_iclr_offset(unsigned long ino)
+{
+	return FIRE_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_ino_to_iclr(unsigned long pbm_regs,
+					    unsigned int ino)
+{
+	return pbm_regs + fire_iclr_offset(ino);
+}
+
+static unsigned long fire_ino_to_imap(unsigned long pbm_regs,
+					    unsigned int ino)
+{
+	return pbm_regs + fire_imap_offset(ino);
+}
+
+static unsigned int fire_irq_build(struct device_node *dp,
+					 unsigned int ino,
+					 void *_data)
+{
+	struct fire_irq_data *irq_data = _data;
+	unsigned long pbm_regs = irq_data->pbm_regs;
+	unsigned long imap, iclr;
+	unsigned long int_ctrlr;
+
+	ino &= 0x3f;
+
+	/* Now build the IRQ bucket. */
+	imap = fire_ino_to_imap(pbm_regs, ino);
+	iclr = fire_ino_to_iclr(pbm_regs, ino);
+
+	/* Set the interrupt controller number.  */
+	int_ctrlr = 1 << 6;
+	upa_writeq(int_ctrlr, imap);
+
+	/* The interrupt map registers do not have an INO field
+	 * like other chips do.  They return zero in the INO
+	 * field, and the interrupt controller number is controlled
+	 * in bits 6 thru 9.  So in order for build_irq() to get
+	 * the INO right we pass it in as part of the fixup
+	 * which will get added to the map register zero value
+	 * read by build_irq().
+	 */
+	ino |= (irq_data->portid << 6);
+	ino -= int_ctrlr;
+	return build_irq(ino, iclr, imap);
+}
+
+static void fire_irq_trans_init(struct device_node *dp)
+{
+	const struct linux_prom64_registers *regs;
+	struct fire_irq_data *irq_data;
+
+	dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+	dp->irq_trans->irq_build = fire_irq_build;
+
+	irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
+
+	regs = of_get_property(dp, "reg", NULL);
+	dp->irq_trans->data = irq_data;
+
+	irq_data->pbm_regs = regs[0].phys_addr;
+	irq_data->portid = of_getintprop_default(dp, "portid", 0);
+}
 #endif /* CONFIG_PCI */
 
 #ifdef CONFIG_SBUS
@@ -1069,6 +1145,7 @@
 	{ "SUNW,tomatillo", tomatillo_irq_trans_init },
 	{ "pci108e,a801", tomatillo_irq_trans_init },
 	{ "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
+	{ "pciex108e,80f0", fire_irq_trans_init },
 };
 #endif