diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2006-02-15 22:48:54 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-03-20 04:13:05 -0500 |
commit | 9d29a3fafd06534ad73427fee3c968c094d05b9b (patch) | |
tree | 4afd7455d6249d9143acea6c4704f69aa98d311a | |
parent | 7890f794e0e6f7dce2a5f4a03ba64b0b3fe306bd (diff) |
[SPARC64]: Decode virtual-devices interrupts correctly.
Need to translate through the interrupt-map{,-mask] properties.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc64/kernel/devices.c | 98 | ||||
-rw-r--r-- | drivers/serial/sunhv.c | 14 | ||||
-rw-r--r-- | include/asm-sparc64/vdev.h | 6 |
3 files changed, 92 insertions, 26 deletions
diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 71eee392e141..1341b99ca7aa 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/timer.h> | 22 | #include <asm/timer.h> |
23 | #include <asm/cpudata.h> | 23 | #include <asm/cpudata.h> |
24 | #include <asm/vdev.h> | 24 | #include <asm/vdev.h> |
25 | #include <asm/irq.h> | ||
25 | 26 | ||
26 | /* Used to synchronize acceses to NatSemi SUPER I/O chip configure | 27 | /* Used to synchronize acceses to NatSemi SUPER I/O chip configure |
27 | * operations in asm/ns87303.h | 28 | * operations in asm/ns87303.h |
@@ -33,14 +34,28 @@ extern void central_probe(void); | |||
33 | 34 | ||
34 | u32 sun4v_vdev_devhandle; | 35 | u32 sun4v_vdev_devhandle; |
35 | int sun4v_vdev_root; | 36 | int sun4v_vdev_root; |
36 | struct linux_prom_pci_intmap *sun4v_vdev_intmap; | 37 | |
37 | int sun4v_vdev_num_intmap; | 38 | struct vdev_intmap { |
38 | struct linux_prom_pci_intmap sun4v_vdev_intmask; | 39 | unsigned int phys; |
40 | unsigned int irq; | ||
41 | unsigned int cnode; | ||
42 | unsigned int cinterrupt; | ||
43 | }; | ||
44 | |||
45 | struct vdev_intmask { | ||
46 | unsigned int phys; | ||
47 | unsigned int interrupt; | ||
48 | unsigned int __unused; | ||
49 | }; | ||
50 | |||
51 | static struct vdev_intmap *vdev_intmap; | ||
52 | static int vdev_num_intmap; | ||
53 | static struct vdev_intmask vdev_intmask; | ||
39 | 54 | ||
40 | static void __init sun4v_virtual_device_probe(void) | 55 | static void __init sun4v_virtual_device_probe(void) |
41 | { | 56 | { |
42 | struct linux_prom64_registers regs; | 57 | struct linux_prom64_registers regs; |
43 | struct linux_prom_pci_intmap *ip; | 58 | struct vdev_intmap *ip; |
44 | int node, sz, err; | 59 | int node, sz, err; |
45 | 60 | ||
46 | if (tlb_type != hypervisor) | 61 | if (tlb_type != hypervisor) |
@@ -58,10 +73,21 @@ static void __init sun4v_virtual_device_probe(void) | |||
58 | prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); | 73 | prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); |
59 | sun4v_vdev_devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; | 74 | sun4v_vdev_devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; |
60 | 75 | ||
61 | sz = sizeof(*ip) * 64; | 76 | sz = prom_getproplen(node, "interrupt-map"); |
62 | sun4v_vdev_intmap = ip = alloc_bootmem_low_pages(sz); | 77 | if (sz <= 0) { |
63 | if (!sun4v_vdev_intmap) { | 78 | prom_printf("SUN4V: Error, no vdev interrupt-map.\n"); |
64 | prom_printf("SUN4V: Error, cannot allocate vdev intmap.\n"); | 79 | prom_halt(); |
80 | } | ||
81 | |||
82 | if ((sz % sizeof(*ip)) != 0) { | ||
83 | prom_printf("SUN4V: Bogus interrupt-map property size %d\n", | ||
84 | sz); | ||
85 | prom_halt(); | ||
86 | } | ||
87 | |||
88 | vdev_intmap = ip = alloc_bootmem_low_pages(sz); | ||
89 | if (!vdev_intmap) { | ||
90 | prom_printf("SUN4V: Error, cannot allocate vdev_intmap.\n"); | ||
65 | prom_halt(); | 91 | prom_halt(); |
66 | } | 92 | } |
67 | 93 | ||
@@ -70,22 +96,70 @@ static void __init sun4v_virtual_device_probe(void) | |||
70 | prom_printf("SUN4V: Fatal error, no vdev interrupt-map.\n"); | 96 | prom_printf("SUN4V: Fatal error, no vdev interrupt-map.\n"); |
71 | prom_halt(); | 97 | prom_halt(); |
72 | } | 98 | } |
99 | if (err != sz) { | ||
100 | prom_printf("SUN4V: Inconsistent interrupt-map size, " | ||
101 | "proplen(%d) vs getprop(%d).\n", sz,err); | ||
102 | prom_halt(); | ||
103 | } | ||
73 | 104 | ||
74 | sun4v_vdev_num_intmap = err / sizeof(*ip); | 105 | vdev_num_intmap = err / sizeof(*ip); |
75 | 106 | ||
76 | err = prom_getproperty(node, "interrupt-map-mask", | 107 | err = prom_getproperty(node, "interrupt-map-mask", |
77 | (char *) &sun4v_vdev_intmask, | 108 | (char *) &vdev_intmask, |
78 | sizeof(sun4v_vdev_intmask)); | 109 | sizeof(vdev_intmask)); |
79 | if (err == -1) { | 110 | if (err <= 0) { |
80 | prom_printf("SUN4V: Fatal error, no vdev " | 111 | prom_printf("SUN4V: Fatal error, no vdev " |
81 | "interrupt-map-mask.\n"); | 112 | "interrupt-map-mask.\n"); |
82 | prom_halt(); | 113 | prom_halt(); |
83 | } | 114 | } |
115 | if (err % sizeof(vdev_intmask)) { | ||
116 | prom_printf("SUN4V: Bogus interrupt-map-mask " | ||
117 | "property size %d\n", err); | ||
118 | prom_halt(); | ||
119 | } | ||
84 | 120 | ||
85 | printk("SUN4V: virtual-devices devhandle[%x]\n", | 121 | printk("SUN4V: virtual-devices devhandle[%x]\n", |
86 | sun4v_vdev_devhandle); | 122 | sun4v_vdev_devhandle); |
87 | } | 123 | } |
88 | 124 | ||
125 | unsigned int sun4v_vdev_device_interrupt(unsigned int dev_node) | ||
126 | { | ||
127 | unsigned int irq, reg; | ||
128 | int err, i; | ||
129 | |||
130 | err = prom_getproperty(dev_node, "interrupts", | ||
131 | (char *) &irq, sizeof(irq)); | ||
132 | if (err <= 0) { | ||
133 | printk("VDEV: Cannot get \"interrupts\" " | ||
134 | "property for OBP node %x\n", dev_node); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | err = prom_getproperty(dev_node, "reg", | ||
139 | (char *) ®, sizeof(reg)); | ||
140 | if (err <= 0) { | ||
141 | printk("VDEV: Cannot get \"reg\" " | ||
142 | "property for OBP node %x\n", dev_node); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | for (i = 0; i < vdev_num_intmap; i++) { | ||
147 | if (vdev_intmap[i].phys == (reg & vdev_intmask.phys) && | ||
148 | vdev_intmap[i].irq == (irq & vdev_intmask.interrupt)) { | ||
149 | irq = vdev_intmap[i].cinterrupt; | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | if (i == vdev_num_intmap) { | ||
155 | printk("VDEV: No matching interrupt map entry " | ||
156 | "for OBP node %x\n", dev_node); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0); | ||
161 | } | ||
162 | |||
89 | static const char *cpu_mid_prop(void) | 163 | static const char *cpu_mid_prop(void) |
90 | { | 164 | { |
91 | if (tlb_type == spitfire) | 165 | if (tlb_type == spitfire) |
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index 71c70d7a998c..cc46206a1065 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <asm/hypervisor.h> | 21 | #include <asm/hypervisor.h> |
22 | #include <asm/spitfire.h> | 22 | #include <asm/spitfire.h> |
23 | #include <asm/vdev.h> | 23 | #include <asm/vdev.h> |
24 | #include <asm/oplib.h> | ||
24 | #include <asm/irq.h> | 25 | #include <asm/irq.h> |
25 | 26 | ||
26 | #if defined(CONFIG_MAGIC_SYSRQ) | 27 | #if defined(CONFIG_MAGIC_SYSRQ) |
@@ -427,7 +428,6 @@ static unsigned int __init get_interrupt(void) | |||
427 | const char *cons_str = "console"; | 428 | const char *cons_str = "console"; |
428 | const char *compat_str = "compatible"; | 429 | const char *compat_str = "compatible"; |
429 | int node = prom_getchild(sun4v_vdev_root); | 430 | int node = prom_getchild(sun4v_vdev_root); |
430 | unsigned int irq; | ||
431 | char buf[64]; | 431 | char buf[64]; |
432 | int err, len; | 432 | int err, len; |
433 | 433 | ||
@@ -449,12 +449,7 @@ static unsigned int __init get_interrupt(void) | |||
449 | /* Ok, the this is the OBP node for the sun4v hypervisor | 449 | /* Ok, the this is the OBP node for the sun4v hypervisor |
450 | * console device. Decode the interrupt. | 450 | * console device. Decode the interrupt. |
451 | */ | 451 | */ |
452 | err = prom_getproperty(node, "interrupts", | 452 | return sun4v_vdev_device_interrupt(node); |
453 | (char *) &irq, sizeof(irq)); | ||
454 | if (err == -1) | ||
455 | return 0; | ||
456 | |||
457 | return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0); | ||
458 | } | 453 | } |
459 | 454 | ||
460 | static u32 sunhv_irq; | 455 | static u32 sunhv_irq; |
@@ -487,8 +482,8 @@ static int __init sunhv_init(void) | |||
487 | return -ENODEV; | 482 | return -ENODEV; |
488 | } | 483 | } |
489 | 484 | ||
490 | printk("SUNHV: SUN4V virtual console, IRQ[%08x]\n", | 485 | printk("SUNHV: SUN4V virtual console, IRQ %s\n", |
491 | sunhv_irq); | 486 | __irq_itoa(sunhv_irq)); |
492 | 487 | ||
493 | sunhv_reg.minor = sunserial_current_minor; | 488 | sunhv_reg.minor = sunserial_current_minor; |
494 | sunhv_reg.nr = 1; | 489 | sunhv_reg.nr = 1; |
@@ -520,7 +515,6 @@ static void __exit sunhv_exit(void) | |||
520 | 515 | ||
521 | uart_remove_one_port(&sunhv_reg, port); | 516 | uart_remove_one_port(&sunhv_reg, port); |
522 | free_irq(sunhv_irq, port); | 517 | free_irq(sunhv_irq, port); |
523 | |||
524 | sunserial_current_minor -= 1; | 518 | sunserial_current_minor -= 1; |
525 | 519 | ||
526 | uart_unregister_driver(&sunhv_reg); | 520 | uart_unregister_driver(&sunhv_reg); |
diff --git a/include/asm-sparc64/vdev.h b/include/asm-sparc64/vdev.h index ebaac41a6e1a..996e6be7b976 100644 --- a/include/asm-sparc64/vdev.h +++ b/include/asm-sparc64/vdev.h | |||
@@ -7,12 +7,10 @@ | |||
7 | #define _SPARC64_VDEV_H | 7 | #define _SPARC64_VDEV_H |
8 | 8 | ||
9 | #include <linux/types.h> | 9 | #include <linux/types.h> |
10 | #include <asm/oplib.h> | ||
11 | 10 | ||
12 | extern u32 sun4v_vdev_devhandle; | 11 | extern u32 sun4v_vdev_devhandle; |
13 | extern int sun4v_vdev_root; | 12 | extern int sun4v_vdev_root; |
14 | extern struct linux_prom_pci_intmap *sun4v_vdev_intmap; | 13 | |
15 | extern int sun4v_vdev_num_intmap; | 14 | extern unsigned int sun4v_vdev_device_interrupt(unsigned int); |
16 | extern struct linux_prom_pci_intmap sun4v_vdev_intmask; | ||
17 | 15 | ||
18 | #endif /* !(_SPARC64_VDEV_H) */ | 16 | #endif /* !(_SPARC64_VDEV_H) */ |