blob: 136f872a8a57b642f665ef6e671f37be0481fb79 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* 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>
David S. Millere77227e2006-02-13 20:42:16 -080015#include <linux/bootmem.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
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>
David S. Millere77227e2006-02-13 20:42:16 -080024#include <asm/vdev.h>
David S. Miller9d29a3f2006-02-15 19:48:54 -080025#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27/* Used to synchronize acceses to NatSemi SUPER I/O chip configure
28 * operations in asm/ns87303.h
29 */
30DEFINE_SPINLOCK(ns87303_lock);
31
32extern void cpu_probe(void);
33extern void central_probe(void);
34
David S. Millere77227e2006-02-13 20:42:16 -080035u32 sun4v_vdev_devhandle;
David S. Miller6760d282006-06-21 22:56:20 -070036struct device_node *sun4v_vdev_root;
David S. Miller9d29a3f2006-02-15 19:48:54 -080037
38struct vdev_intmap {
39 unsigned int phys;
40 unsigned int irq;
41 unsigned int cnode;
42 unsigned int cinterrupt;
43};
44
45struct vdev_intmask {
46 unsigned int phys;
47 unsigned int interrupt;
48 unsigned int __unused;
49};
50
51static struct vdev_intmap *vdev_intmap;
52static int vdev_num_intmap;
David S. Miller6760d282006-06-21 22:56:20 -070053static struct vdev_intmask *vdev_intmask;
David S. Millere77227e2006-02-13 20:42:16 -080054
55static void __init sun4v_virtual_device_probe(void)
56{
David S. Miller6760d282006-06-21 22:56:20 -070057 struct linux_prom64_registers *regs;
58 struct property *prop;
59 struct device_node *dp;
60 int sz;
David S. Millere77227e2006-02-13 20:42:16 -080061
62 if (tlb_type != hypervisor)
63 return;
64
David S. Miller6760d282006-06-21 22:56:20 -070065 dp = of_find_node_by_name(NULL, "virtual-devices");
66 if (!dp) {
David S. Millere77227e2006-02-13 20:42:16 -080067 prom_printf("SUN4V: Fatal error, no virtual-devices node.\n");
68 prom_halt();
69 }
70
David S. Miller6760d282006-06-21 22:56:20 -070071 sun4v_vdev_root = dp;
David S. Millere77227e2006-02-13 20:42:16 -080072
David S. Miller6760d282006-06-21 22:56:20 -070073 prop = of_find_property(dp, "reg", NULL);
74 regs = prop->value;
75 sun4v_vdev_devhandle = (regs[0].phys_addr >> 32UL) & 0x0fffffff;
David S. Millere77227e2006-02-13 20:42:16 -080076
David S. Miller6760d282006-06-21 22:56:20 -070077 prop = of_find_property(dp, "interrupt-map", &sz);
78 vdev_intmap = prop->value;
79 vdev_num_intmap = sz / sizeof(struct vdev_intmap);
David S. Miller9d29a3f2006-02-15 19:48:54 -080080
David S. Miller6760d282006-06-21 22:56:20 -070081 prop = of_find_property(dp, "interrupt-map-mask", NULL);
82 vdev_intmask = prop->value;
David S. Miller9d29a3f2006-02-15 19:48:54 -080083
David S. Miller6760d282006-06-21 22:56:20 -070084 printk("%s: Virtual Device Bus devhandle[%x]\n",
85 dp->full_name, sun4v_vdev_devhandle);
David S. Millere77227e2006-02-13 20:42:16 -080086}
87
David S. Miller6760d282006-06-21 22:56:20 -070088unsigned int sun4v_vdev_device_interrupt(struct device_node *dev_node)
David S. Miller9d29a3f2006-02-15 19:48:54 -080089{
David S. Miller6760d282006-06-21 22:56:20 -070090 struct property *prop;
David S. Miller9d29a3f2006-02-15 19:48:54 -080091 unsigned int irq, reg;
David S. Miller6760d282006-06-21 22:56:20 -070092 int i;
David S. Miller9d29a3f2006-02-15 19:48:54 -080093
David S. Miller6760d282006-06-21 22:56:20 -070094 prop = of_find_property(dev_node, "interrupts", NULL);
95 if (!prop) {
David S. Miller9d29a3f2006-02-15 19:48:54 -080096 printk("VDEV: Cannot get \"interrupts\" "
David S. Miller6760d282006-06-21 22:56:20 -070097 "property for OBP node %s\n",
98 dev_node->full_name);
David S. Miller9d29a3f2006-02-15 19:48:54 -080099 return 0;
100 }
David S. Miller6760d282006-06-21 22:56:20 -0700101 irq = *(unsigned int *) prop->value;
David S. Miller9d29a3f2006-02-15 19:48:54 -0800102
David S. Miller6760d282006-06-21 22:56:20 -0700103 prop = of_find_property(dev_node, "reg", NULL);
104 if (!prop) {
David S. Miller9d29a3f2006-02-15 19:48:54 -0800105 printk("VDEV: Cannot get \"reg\" "
David S. Miller6760d282006-06-21 22:56:20 -0700106 "property for OBP node %s\n",
107 dev_node->full_name);
David S. Miller9d29a3f2006-02-15 19:48:54 -0800108 return 0;
109 }
David S. Miller6760d282006-06-21 22:56:20 -0700110 reg = *(unsigned int *) prop->value;
David S. Miller9d29a3f2006-02-15 19:48:54 -0800111
112 for (i = 0; i < vdev_num_intmap; i++) {
David S. Miller6760d282006-06-21 22:56:20 -0700113 if (vdev_intmap[i].phys == (reg & vdev_intmask->phys) &&
114 vdev_intmap[i].irq == (irq & vdev_intmask->interrupt)) {
David S. Miller9d29a3f2006-02-15 19:48:54 -0800115 irq = vdev_intmap[i].cinterrupt;
116 break;
117 }
118 }
119
120 if (i == vdev_num_intmap) {
121 printk("VDEV: No matching interrupt map entry "
David S. Miller6760d282006-06-21 22:56:20 -0700122 "for OBP node %s\n", dev_node->full_name);
David S. Miller9d29a3f2006-02-15 19:48:54 -0800123 return 0;
124 }
125
David S. Millere18e2a02006-06-20 01:23:32 -0700126 return sun4v_build_irq(sun4v_vdev_devhandle, irq);
David S. Miller9d29a3f2006-02-15 19:48:54 -0800127}
128
David S. Miller4cce4b72006-02-09 20:46:22 -0800129static const char *cpu_mid_prop(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
131 if (tlb_type == spitfire)
132 return "upa-portid";
133 return "portid";
134}
135
David S. Miller4cce4b72006-02-09 20:46:22 -0800136static int get_cpu_mid(int prom_node)
137{
138 if (tlb_type == hypervisor) {
139 struct linux_prom64_registers reg;
140
141 if (prom_getproplen(prom_node, "cpuid") == 4)
142 return prom_getintdefault(prom_node, "cpuid", 0);
143
144 prom_getproperty(prom_node, "reg", (char *) &reg, sizeof(reg));
145 return (reg.phys_addr >> 32) & 0x0fffffffUL;
146 } else {
147 const char *prop_name = cpu_mid_prop();
148
149 return prom_getintdefault(prom_node, prop_name, 0);
150 }
151}
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153static int check_cpu_node(int nd, int *cur_inst,
154 int (*compare)(int, int, void *), void *compare_arg,
155 int *prom_node, int *mid)
156{
157 char node_str[128];
158
159 prom_getstring(nd, "device_type", node_str, sizeof(node_str));
160 if (strcmp(node_str, "cpu"))
161 return -ENODEV;
162
163 if (!compare(nd, *cur_inst, compare_arg)) {
164 if (prom_node)
165 *prom_node = nd;
166 if (mid)
David S. Miller4cce4b72006-02-09 20:46:22 -0800167 *mid = get_cpu_mid(nd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return 0;
169 }
170
171 (*cur_inst)++;
172
173 return -ENODEV;
174}
175
176static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg,
177 int *prom_node, int *mid)
178{
179 int nd, cur_inst, err;
180
181 nd = prom_root_node;
182 cur_inst = 0;
183
184 err = check_cpu_node(nd, &cur_inst,
185 compare, compare_arg,
186 prom_node, mid);
187 if (err == 0)
188 return 0;
189
190 nd = prom_getchild(nd);
191 while ((nd = prom_getsibling(nd)) != 0) {
192 err = check_cpu_node(nd, &cur_inst,
193 compare, compare_arg,
194 prom_node, mid);
195 if (err == 0)
196 return 0;
197 }
198
199 return -ENODEV;
200}
201
202static int cpu_instance_compare(int nd, int instance, void *_arg)
203{
204 int desired_instance = (int) (long) _arg;
205
206 if (instance == desired_instance)
207 return 0;
208 return -ENODEV;
209}
210
211int cpu_find_by_instance(int instance, int *prom_node, int *mid)
212{
213 return __cpu_find_by(cpu_instance_compare, (void *)(long)instance,
214 prom_node, mid);
215}
216
217static int cpu_mid_compare(int nd, int instance, void *_arg)
218{
219 int desired_mid = (int) (long) _arg;
220 int this_mid;
221
David S. Miller4cce4b72006-02-09 20:46:22 -0800222 this_mid = get_cpu_mid(nd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 if (this_mid == desired_mid)
224 return 0;
225 return -ENODEV;
226}
227
228int cpu_find_by_mid(int mid, int *prom_node)
229{
230 return __cpu_find_by(cpu_mid_compare, (void *)(long)mid,
231 prom_node, NULL);
232}
233
234void __init device_scan(void)
235{
236 /* FIX ME FAST... -DaveM */
237 ioport_resource.end = 0xffffffffffffffffUL;
238
239 prom_printf("Booting Linux...\n");
240
241#ifndef CONFIG_SMP
242 {
David S. Millerf03b8a52006-02-15 00:35:50 -0800243 int err, cpu_node, def;
244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 err = cpu_find_by_instance(0, &cpu_node, NULL);
246 if (err) {
247 prom_printf("No cpu nodes, cannot continue\n");
248 prom_halt();
249 }
250 cpu_data(0).clock_tick = prom_getintdefault(cpu_node,
251 "clock-frequency",
252 0);
David S. Millerf03b8a52006-02-15 00:35:50 -0800253
254 def = ((tlb_type == hypervisor) ?
255 (8 * 1024) :
256 (16 * 1024));
David S. Miller80dc0d62005-09-26 00:32:17 -0700257 cpu_data(0).dcache_size = prom_getintdefault(cpu_node,
258 "dcache-size",
David S. Millerf03b8a52006-02-15 00:35:50 -0800259 def);
260
261 def = 32;
David S. Miller80dc0d62005-09-26 00:32:17 -0700262 cpu_data(0).dcache_line_size =
David S. Millerf03b8a52006-02-15 00:35:50 -0800263 prom_getintdefault(cpu_node, "dcache-line-size",
264 def);
265
266 def = 16 * 1024;
David S. Miller80dc0d62005-09-26 00:32:17 -0700267 cpu_data(0).icache_size = prom_getintdefault(cpu_node,
268 "icache-size",
David S. Millerf03b8a52006-02-15 00:35:50 -0800269 def);
270
271 def = 32;
David S. Miller80dc0d62005-09-26 00:32:17 -0700272 cpu_data(0).icache_line_size =
David S. Millerf03b8a52006-02-15 00:35:50 -0800273 prom_getintdefault(cpu_node, "icache-line-size",
274 def);
275
276 def = ((tlb_type == hypervisor) ?
277 (3 * 1024 * 1024) :
278 (4 * 1024 * 1024));
David S. Miller80dc0d62005-09-26 00:32:17 -0700279 cpu_data(0).ecache_size = prom_getintdefault(cpu_node,
280 "ecache-size",
David S. Millerf03b8a52006-02-15 00:35:50 -0800281 def);
282
283 def = 64;
David S. Miller80dc0d62005-09-26 00:32:17 -0700284 cpu_data(0).ecache_line_size =
David S. Millerf03b8a52006-02-15 00:35:50 -0800285 prom_getintdefault(cpu_node, "ecache-line-size",
286 def);
David S. Miller80dc0d62005-09-26 00:32:17 -0700287 printk("CPU[0]: Caches "
288 "D[sz(%d):line_sz(%d)] "
289 "I[sz(%d):line_sz(%d)] "
290 "E[sz(%d):line_sz(%d)]\n",
291 cpu_data(0).dcache_size, cpu_data(0).dcache_line_size,
292 cpu_data(0).icache_size, cpu_data(0).icache_line_size,
293 cpu_data(0).ecache_size, cpu_data(0).ecache_line_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 }
295#endif
296
David S. Millere77227e2006-02-13 20:42:16 -0800297 sun4v_virtual_device_probe();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 central_probe();
299
300 cpu_probe();
301}