Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* devices.c: Initial scan of the prom device tree for important |
2 | * Sparc device nodes which we need to find. | |
3 | * | |
4 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) | |
5 | */ | |
6 | ||
7 | #include <linux/config.h> | |
8 | #include <linux/kernel.h> | |
9 | #include <linux/threads.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/ioport.h> | |
12 | #include <linux/string.h> | |
13 | #include <linux/spinlock.h> | |
14 | #include <linux/errno.h> | |
e77227eb | 15 | #include <linux/bootmem.h> |
1da177e4 LT |
16 | |
17 | #include <asm/page.h> | |
18 | #include <asm/oplib.h> | |
19 | #include <asm/system.h> | |
20 | #include <asm/smp.h> | |
21 | #include <asm/spitfire.h> | |
22 | #include <asm/timer.h> | |
23 | #include <asm/cpudata.h> | |
e77227eb | 24 | #include <asm/vdev.h> |
9d29a3fa | 25 | #include <asm/irq.h> |
1da177e4 LT |
26 | |
27 | /* Used to synchronize acceses to NatSemi SUPER I/O chip configure | |
28 | * operations in asm/ns87303.h | |
29 | */ | |
30 | DEFINE_SPINLOCK(ns87303_lock); | |
31 | ||
32 | extern void cpu_probe(void); | |
33 | extern void central_probe(void); | |
34 | ||
e77227eb DM |
35 | u32 sun4v_vdev_devhandle; |
36 | int sun4v_vdev_root; | |
9d29a3fa DM |
37 | |
38 | struct vdev_intmap { | |
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; | |
e77227eb DM |
54 | |
55 | static void __init sun4v_virtual_device_probe(void) | |
56 | { | |
57 | struct linux_prom64_registers regs; | |
9d29a3fa | 58 | struct vdev_intmap *ip; |
e77227eb DM |
59 | int node, sz, err; |
60 | ||
61 | if (tlb_type != hypervisor) | |
62 | return; | |
63 | ||
64 | node = prom_getchild(prom_root_node); | |
65 | node = prom_searchsiblings(node, "virtual-devices"); | |
66 | if (!node) { | |
67 | prom_printf("SUN4V: Fatal error, no virtual-devices node.\n"); | |
68 | prom_halt(); | |
69 | } | |
70 | ||
71 | sun4v_vdev_root = node; | |
72 | ||
73 | prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); | |
74 | sun4v_vdev_devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; | |
75 | ||
9d29a3fa DM |
76 | sz = prom_getproplen(node, "interrupt-map"); |
77 | if (sz <= 0) { | |
78 | prom_printf("SUN4V: Error, no vdev interrupt-map.\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"); | |
e77227eb DM |
91 | prom_halt(); |
92 | } | |
93 | ||
94 | err = prom_getproperty(node, "interrupt-map", (char *) ip, sz); | |
95 | if (err == -1) { | |
96 | prom_printf("SUN4V: Fatal error, no vdev interrupt-map.\n"); | |
97 | prom_halt(); | |
98 | } | |
9d29a3fa DM |
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 | } | |
e77227eb | 104 | |
9d29a3fa | 105 | vdev_num_intmap = err / sizeof(*ip); |
e77227eb DM |
106 | |
107 | err = prom_getproperty(node, "interrupt-map-mask", | |
9d29a3fa DM |
108 | (char *) &vdev_intmask, |
109 | sizeof(vdev_intmask)); | |
110 | if (err <= 0) { | |
e77227eb DM |
111 | prom_printf("SUN4V: Fatal error, no vdev " |
112 | "interrupt-map-mask.\n"); | |
113 | prom_halt(); | |
114 | } | |
9d29a3fa DM |
115 | if (err % sizeof(vdev_intmask)) { |
116 | prom_printf("SUN4V: Bogus interrupt-map-mask " | |
117 | "property size %d\n", err); | |
118 | prom_halt(); | |
119 | } | |
e77227eb DM |
120 | |
121 | printk("SUN4V: virtual-devices devhandle[%x]\n", | |
122 | sun4v_vdev_devhandle); | |
123 | } | |
124 | ||
9d29a3fa DM |
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 | ||
ee29074d | 160 | return sun4v_build_irq(sun4v_vdev_devhandle, irq, 5, 0); |
9d29a3fa DM |
161 | } |
162 | ||
4cce4b7c | 163 | static const char *cpu_mid_prop(void) |
1da177e4 LT |
164 | { |
165 | if (tlb_type == spitfire) | |
166 | return "upa-portid"; | |
167 | return "portid"; | |
168 | } | |
169 | ||
4cce4b7c DM |
170 | static int get_cpu_mid(int prom_node) |
171 | { | |
172 | if (tlb_type == hypervisor) { | |
173 | struct linux_prom64_registers reg; | |
174 | ||
175 | if (prom_getproplen(prom_node, "cpuid") == 4) | |
176 | return prom_getintdefault(prom_node, "cpuid", 0); | |
177 | ||
178 | prom_getproperty(prom_node, "reg", (char *) ®, sizeof(reg)); | |
179 | return (reg.phys_addr >> 32) & 0x0fffffffUL; | |
180 | } else { | |
181 | const char *prop_name = cpu_mid_prop(); | |
182 | ||
183 | return prom_getintdefault(prom_node, prop_name, 0); | |
184 | } | |
185 | } | |
186 | ||
1da177e4 LT |
187 | static int check_cpu_node(int nd, int *cur_inst, |
188 | int (*compare)(int, int, void *), void *compare_arg, | |
189 | int *prom_node, int *mid) | |
190 | { | |
191 | char node_str[128]; | |
192 | ||
193 | prom_getstring(nd, "device_type", node_str, sizeof(node_str)); | |
194 | if (strcmp(node_str, "cpu")) | |
195 | return -ENODEV; | |
196 | ||
197 | if (!compare(nd, *cur_inst, compare_arg)) { | |
198 | if (prom_node) | |
199 | *prom_node = nd; | |
200 | if (mid) | |
4cce4b7c | 201 | *mid = get_cpu_mid(nd); |
1da177e4 LT |
202 | return 0; |
203 | } | |
204 | ||
205 | (*cur_inst)++; | |
206 | ||
207 | return -ENODEV; | |
208 | } | |
209 | ||
210 | static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg, | |
211 | int *prom_node, int *mid) | |
212 | { | |
213 | int nd, cur_inst, err; | |
214 | ||
215 | nd = prom_root_node; | |
216 | cur_inst = 0; | |
217 | ||
218 | err = check_cpu_node(nd, &cur_inst, | |
219 | compare, compare_arg, | |
220 | prom_node, mid); | |
221 | if (err == 0) | |
222 | return 0; | |
223 | ||
224 | nd = prom_getchild(nd); | |
225 | while ((nd = prom_getsibling(nd)) != 0) { | |
226 | err = check_cpu_node(nd, &cur_inst, | |
227 | compare, compare_arg, | |
228 | prom_node, mid); | |
229 | if (err == 0) | |
230 | return 0; | |
231 | } | |
232 | ||
233 | return -ENODEV; | |
234 | } | |
235 | ||
236 | static int cpu_instance_compare(int nd, int instance, void *_arg) | |
237 | { | |
238 | int desired_instance = (int) (long) _arg; | |
239 | ||
240 | if (instance == desired_instance) | |
241 | return 0; | |
242 | return -ENODEV; | |
243 | } | |
244 | ||
245 | int cpu_find_by_instance(int instance, int *prom_node, int *mid) | |
246 | { | |
247 | return __cpu_find_by(cpu_instance_compare, (void *)(long)instance, | |
248 | prom_node, mid); | |
249 | } | |
250 | ||
251 | static int cpu_mid_compare(int nd, int instance, void *_arg) | |
252 | { | |
253 | int desired_mid = (int) (long) _arg; | |
254 | int this_mid; | |
255 | ||
4cce4b7c | 256 | this_mid = get_cpu_mid(nd); |
1da177e4 LT |
257 | if (this_mid == desired_mid) |
258 | return 0; | |
259 | return -ENODEV; | |
260 | } | |
261 | ||
262 | int cpu_find_by_mid(int mid, int *prom_node) | |
263 | { | |
264 | return __cpu_find_by(cpu_mid_compare, (void *)(long)mid, | |
265 | prom_node, NULL); | |
266 | } | |
267 | ||
268 | void __init device_scan(void) | |
269 | { | |
270 | /* FIX ME FAST... -DaveM */ | |
271 | ioport_resource.end = 0xffffffffffffffffUL; | |
272 | ||
273 | prom_printf("Booting Linux...\n"); | |
274 | ||
275 | #ifndef CONFIG_SMP | |
276 | { | |
f03b8a54 DM |
277 | int err, cpu_node, def; |
278 | ||
1da177e4 LT |
279 | err = cpu_find_by_instance(0, &cpu_node, NULL); |
280 | if (err) { | |
281 | prom_printf("No cpu nodes, cannot continue\n"); | |
282 | prom_halt(); | |
283 | } | |
284 | cpu_data(0).clock_tick = prom_getintdefault(cpu_node, | |
285 | "clock-frequency", | |
286 | 0); | |
f03b8a54 DM |
287 | |
288 | def = ((tlb_type == hypervisor) ? | |
289 | (8 * 1024) : | |
290 | (16 * 1024)); | |
80dc0d6b DM |
291 | cpu_data(0).dcache_size = prom_getintdefault(cpu_node, |
292 | "dcache-size", | |
f03b8a54 DM |
293 | def); |
294 | ||
295 | def = 32; | |
80dc0d6b | 296 | cpu_data(0).dcache_line_size = |
f03b8a54 DM |
297 | prom_getintdefault(cpu_node, "dcache-line-size", |
298 | def); | |
299 | ||
300 | def = 16 * 1024; | |
80dc0d6b DM |
301 | cpu_data(0).icache_size = prom_getintdefault(cpu_node, |
302 | "icache-size", | |
f03b8a54 DM |
303 | def); |
304 | ||
305 | def = 32; | |
80dc0d6b | 306 | cpu_data(0).icache_line_size = |
f03b8a54 DM |
307 | prom_getintdefault(cpu_node, "icache-line-size", |
308 | def); | |
309 | ||
310 | def = ((tlb_type == hypervisor) ? | |
311 | (3 * 1024 * 1024) : | |
312 | (4 * 1024 * 1024)); | |
80dc0d6b DM |
313 | cpu_data(0).ecache_size = prom_getintdefault(cpu_node, |
314 | "ecache-size", | |
f03b8a54 DM |
315 | def); |
316 | ||
317 | def = 64; | |
80dc0d6b | 318 | cpu_data(0).ecache_line_size = |
f03b8a54 DM |
319 | prom_getintdefault(cpu_node, "ecache-line-size", |
320 | def); | |
80dc0d6b DM |
321 | printk("CPU[0]: Caches " |
322 | "D[sz(%d):line_sz(%d)] " | |
323 | "I[sz(%d):line_sz(%d)] " | |
324 | "E[sz(%d):line_sz(%d)]\n", | |
325 | cpu_data(0).dcache_size, cpu_data(0).dcache_line_size, | |
326 | cpu_data(0).icache_size, cpu_data(0).icache_line_size, | |
327 | cpu_data(0).ecache_size, cpu_data(0).ecache_line_size); | |
1da177e4 LT |
328 | } |
329 | #endif | |
330 | ||
e77227eb | 331 | sun4v_virtual_device_probe(); |
1da177e4 LT |
332 | central_probe(); |
333 | ||
334 | cpu_probe(); | |
335 | } |