Эх сурвалжийг харах

[SPARC64]: Decode virtual-devices interrupts correctly.

Need to translate through the interrupt-map{,-mask] properties.

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 19 жил өмнө
parent
commit
9d29a3fafd

+ 86 - 12
arch/sparc64/kernel/devices.c

@@ -22,6 +22,7 @@
 #include <asm/timer.h>
 #include <asm/cpudata.h>
 #include <asm/vdev.h>
+#include <asm/irq.h>
 
 /* Used to synchronize acceses to NatSemi SUPER I/O chip configure
  * operations in asm/ns87303.h
@@ -33,14 +34,28 @@ extern void central_probe(void);
 
 u32 sun4v_vdev_devhandle;
 int sun4v_vdev_root;
-struct linux_prom_pci_intmap *sun4v_vdev_intmap;
-int sun4v_vdev_num_intmap;
-struct linux_prom_pci_intmap sun4v_vdev_intmask;
+
+struct vdev_intmap {
+	unsigned int phys;
+	unsigned int irq;
+	unsigned int cnode;
+	unsigned int cinterrupt;
+};
+
+struct vdev_intmask {
+	unsigned int phys;
+	unsigned int interrupt;
+	unsigned int __unused;
+};
+
+static struct vdev_intmap *vdev_intmap;
+static int vdev_num_intmap;
+static struct vdev_intmask vdev_intmask;
 
 static void __init sun4v_virtual_device_probe(void)
 {
 	struct linux_prom64_registers regs;
-	struct linux_prom_pci_intmap *ip;
+	struct vdev_intmap *ip;
 	int node, sz, err;
 
 	if (tlb_type != hypervisor)
@@ -58,10 +73,21 @@ static void __init sun4v_virtual_device_probe(void)
 	prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
 	sun4v_vdev_devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff;
 
-	sz = sizeof(*ip) * 64;
-	sun4v_vdev_intmap = ip = alloc_bootmem_low_pages(sz);
-	if (!sun4v_vdev_intmap) {
-		prom_printf("SUN4V: Error, cannot allocate vdev intmap.\n");
+	sz = prom_getproplen(node, "interrupt-map");
+	if (sz <= 0) {
+		prom_printf("SUN4V: Error, no vdev interrupt-map.\n");
+		prom_halt();
+	}
+
+	if ((sz % sizeof(*ip)) != 0) {
+		prom_printf("SUN4V: Bogus interrupt-map property size %d\n",
+			    sz);
+		prom_halt();
+	}
+
+	vdev_intmap = ip = alloc_bootmem_low_pages(sz);
+	if (!vdev_intmap) {
+		prom_printf("SUN4V: Error, cannot allocate vdev_intmap.\n");
 		prom_halt();
 	}
 
@@ -70,22 +96,70 @@ static void __init sun4v_virtual_device_probe(void)
 		prom_printf("SUN4V: Fatal error, no vdev interrupt-map.\n");
 		prom_halt();
 	}
+	if (err != sz) {
+		prom_printf("SUN4V: Inconsistent interrupt-map size, "
+			    "proplen(%d) vs getprop(%d).\n", sz,err);
+		prom_halt();
+	}
 
-	sun4v_vdev_num_intmap = err / sizeof(*ip);
+	vdev_num_intmap = err / sizeof(*ip);
 
 	err = prom_getproperty(node, "interrupt-map-mask",
-			       (char *) &sun4v_vdev_intmask,
-			       sizeof(sun4v_vdev_intmask));
-	if (err == -1) {
+			       (char *) &vdev_intmask,
+			       sizeof(vdev_intmask));
+	if (err <= 0) {
 		prom_printf("SUN4V: Fatal error, no vdev "
 			    "interrupt-map-mask.\n");
 		prom_halt();
 	}
+	if (err % sizeof(vdev_intmask)) {
+		prom_printf("SUN4V: Bogus interrupt-map-mask "
+			    "property size %d\n", err);
+		prom_halt();
+	}
 
 	printk("SUN4V: virtual-devices devhandle[%x]\n",
 	       sun4v_vdev_devhandle);
 }
 
+unsigned int sun4v_vdev_device_interrupt(unsigned int dev_node)
+{
+	unsigned int irq, reg;
+	int err, i;
+
+	err = prom_getproperty(dev_node, "interrupts",
+			       (char *) &irq, sizeof(irq));
+	if (err <= 0) {
+		printk("VDEV: Cannot get \"interrupts\" "
+		       "property for OBP node %x\n", dev_node);
+		return 0;
+	}
+
+	err = prom_getproperty(dev_node, "reg",
+			       (char *) &reg, sizeof(reg));
+	if (err <= 0) {
+		printk("VDEV: Cannot get \"reg\" "
+		       "property for OBP node %x\n", dev_node);
+		return 0;
+	}
+
+	for (i = 0; i < vdev_num_intmap; i++) {
+		if (vdev_intmap[i].phys == (reg & vdev_intmask.phys) &&
+		    vdev_intmap[i].irq == (irq & vdev_intmask.interrupt)) {
+			irq = vdev_intmap[i].cinterrupt;
+			break;
+		}
+	}
+
+	if (i == vdev_num_intmap) {
+		printk("VDEV: No matching interrupt map entry "
+		       "for OBP node %x\n", dev_node);
+		return 0;
+	}
+
+	return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0);
+}
+
 static const char *cpu_mid_prop(void)
 {
 	if (tlb_type == spitfire)

+ 4 - 10
drivers/serial/sunhv.c

@@ -21,6 +21,7 @@
 #include <asm/hypervisor.h>
 #include <asm/spitfire.h>
 #include <asm/vdev.h>
+#include <asm/oplib.h>
 #include <asm/irq.h>
 
 #if defined(CONFIG_MAGIC_SYSRQ)
@@ -427,7 +428,6 @@ static unsigned int __init get_interrupt(void)
 	const char *cons_str = "console";
 	const char *compat_str = "compatible";
 	int node = prom_getchild(sun4v_vdev_root);
-	unsigned int irq;
 	char buf[64];
 	int err, len;
 
@@ -449,12 +449,7 @@ static unsigned int __init get_interrupt(void)
 	/* Ok, the this is the OBP node for the sun4v hypervisor
 	 * console device.  Decode the interrupt.
 	 */
-	err = prom_getproperty(node, "interrupts",
-			       (char *) &irq, sizeof(irq));
-	if (err == -1)
-		return 0;
-
-	return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0);
+	return sun4v_vdev_device_interrupt(node);
 }
 
 static u32 sunhv_irq;
@@ -487,8 +482,8 @@ static int __init sunhv_init(void)
 		return -ENODEV;
 	}
 
-	printk("SUNHV: SUN4V virtual console, IRQ[%08x]\n",
-	       sunhv_irq);
+	printk("SUNHV: SUN4V virtual console, IRQ %s\n",
+	       __irq_itoa(sunhv_irq));
 
 	sunhv_reg.minor = sunserial_current_minor;
 	sunhv_reg.nr = 1;
@@ -520,7 +515,6 @@ static void __exit sunhv_exit(void)
 
 	uart_remove_one_port(&sunhv_reg, port);
 	free_irq(sunhv_irq, port);
-
 	sunserial_current_minor -= 1;
 
 	uart_unregister_driver(&sunhv_reg);

+ 2 - 4
include/asm-sparc64/vdev.h

@@ -7,12 +7,10 @@
 #define _SPARC64_VDEV_H
 
 #include <linux/types.h>
-#include <asm/oplib.h>
 
 extern u32 sun4v_vdev_devhandle;
 extern int sun4v_vdev_root;
-extern struct linux_prom_pci_intmap *sun4v_vdev_intmap;
-extern int sun4v_vdev_num_intmap;
-extern struct linux_prom_pci_intmap sun4v_vdev_intmask;
+
+extern unsigned int sun4v_vdev_device_interrupt(unsigned int);
 
 #endif /* !(_SPARC64_VDEV_H) */