diff options
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/devices.c | 98 |
1 files changed, 86 insertions, 12 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) |