blob: 389301c95cb28408269de423d066249acaa7b38d [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. Miller07f8e5f2006-06-21 23:34:02 -0700136static int get_cpu_mid(struct device_node *dp)
David S. Miller4cce4b72006-02-09 20:46:22 -0800137{
David S. Miller07f8e5f2006-06-21 23:34:02 -0700138 struct property *prop;
139
David S. Miller4cce4b72006-02-09 20:46:22 -0800140 if (tlb_type == hypervisor) {
David S. Miller07f8e5f2006-06-21 23:34:02 -0700141 struct linux_prom64_registers *reg;
142 int len;
David S. Miller4cce4b72006-02-09 20:46:22 -0800143
David S. Miller07f8e5f2006-06-21 23:34:02 -0700144 prop = of_find_property(dp, "cpuid", &len);
145 if (prop && len == 4)
146 return *(int *) prop->value;
David S. Miller4cce4b72006-02-09 20:46:22 -0800147
David S. Miller07f8e5f2006-06-21 23:34:02 -0700148 prop = of_find_property(dp, "reg", NULL);
149 reg = prop->value;
150 return (reg[0].phys_addr >> 32) & 0x0fffffffUL;
David S. Miller4cce4b72006-02-09 20:46:22 -0800151 } else {
152 const char *prop_name = cpu_mid_prop();
153
David S. Miller07f8e5f2006-06-21 23:34:02 -0700154 prop = of_find_property(dp, prop_name, NULL);
155 if (prop)
156 return *(int *) prop->value;
157 return 0;
David S. Miller4cce4b72006-02-09 20:46:22 -0800158 }
159}
160
David S. Miller07f8e5f2006-06-21 23:34:02 -0700161static int check_cpu_node(struct device_node *dp, int *cur_inst,
162 int (*compare)(struct device_node *, int, void *),
163 void *compare_arg,
164 struct device_node **dev_node, int *mid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
David S. Miller07f8e5f2006-06-21 23:34:02 -0700166 if (strcmp(dp->type, "cpu"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return -ENODEV;
168
David S. Miller07f8e5f2006-06-21 23:34:02 -0700169 if (!compare(dp, *cur_inst, compare_arg)) {
170 if (dev_node)
171 *dev_node = dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 if (mid)
David S. Miller07f8e5f2006-06-21 23:34:02 -0700173 *mid = get_cpu_mid(dp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return 0;
175 }
176
177 (*cur_inst)++;
178
179 return -ENODEV;
180}
181
David S. Miller07f8e5f2006-06-21 23:34:02 -0700182static int __cpu_find_by(int (*compare)(struct device_node *, int, void *),
183 void *compare_arg,
184 struct device_node **dev_node, int *mid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
David S. Miller07f8e5f2006-06-21 23:34:02 -0700186 struct device_node *dp;
187 int cur_inst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 cur_inst = 0;
David S. Miller07f8e5f2006-06-21 23:34:02 -0700190 for_each_node_by_type(dp, "cpu") {
191 int err = check_cpu_node(dp, &cur_inst,
192 compare, compare_arg,
193 dev_node, mid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 if (err == 0)
195 return 0;
196 }
197
198 return -ENODEV;
199}
200
David S. Miller07f8e5f2006-06-21 23:34:02 -0700201static int cpu_instance_compare(struct device_node *dp, int instance, void *_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 int desired_instance = (int) (long) _arg;
204
205 if (instance == desired_instance)
206 return 0;
207 return -ENODEV;
208}
209
David S. Miller07f8e5f2006-06-21 23:34:02 -0700210int cpu_find_by_instance(int instance, struct device_node **dev_node, int *mid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 return __cpu_find_by(cpu_instance_compare, (void *)(long)instance,
David S. Miller07f8e5f2006-06-21 23:34:02 -0700213 dev_node, mid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214}
215
David S. Miller07f8e5f2006-06-21 23:34:02 -0700216static int cpu_mid_compare(struct device_node *dp, int instance, void *_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
218 int desired_mid = (int) (long) _arg;
219 int this_mid;
220
David S. Miller07f8e5f2006-06-21 23:34:02 -0700221 this_mid = get_cpu_mid(dp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 if (this_mid == desired_mid)
223 return 0;
224 return -ENODEV;
225}
226
David S. Miller07f8e5f2006-06-21 23:34:02 -0700227int cpu_find_by_mid(int mid, struct device_node **dev_node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 return __cpu_find_by(cpu_mid_compare, (void *)(long)mid,
David S. Miller07f8e5f2006-06-21 23:34:02 -0700230 dev_node, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
233void __init device_scan(void)
234{
235 /* FIX ME FAST... -DaveM */
236 ioport_resource.end = 0xffffffffffffffffUL;
237
238 prom_printf("Booting Linux...\n");
239
240#ifndef CONFIG_SMP
241 {
David S. Miller07f8e5f2006-06-21 23:34:02 -0700242 struct device_node *dp;
243 int err, def;
David S. Millerf03b8a52006-02-15 00:35:50 -0800244
David S. Miller07f8e5f2006-06-21 23:34:02 -0700245 err = cpu_find_by_instance(0, &dp, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 if (err) {
247 prom_printf("No cpu nodes, cannot continue\n");
248 prom_halt();
249 }
David S. Miller07f8e5f2006-06-21 23:34:02 -0700250 cpu_data(0).clock_tick =
251 of_getintprop_default(dp, "clock-frequency", 0);
David S. Millerf03b8a52006-02-15 00:35:50 -0800252
253 def = ((tlb_type == hypervisor) ?
254 (8 * 1024) :
255 (16 * 1024));
David S. Miller07f8e5f2006-06-21 23:34:02 -0700256 cpu_data(0).dcache_size = of_getintprop_default(dp,
257 "dcache-size",
258 def);
David S. Millerf03b8a52006-02-15 00:35:50 -0800259
260 def = 32;
David S. Miller80dc0d62005-09-26 00:32:17 -0700261 cpu_data(0).dcache_line_size =
David S. Miller07f8e5f2006-06-21 23:34:02 -0700262 of_getintprop_default(dp, "dcache-line-size", def);
David S. Millerf03b8a52006-02-15 00:35:50 -0800263
264 def = 16 * 1024;
David S. Miller07f8e5f2006-06-21 23:34:02 -0700265 cpu_data(0).icache_size = of_getintprop_default(dp,
266 "icache-size",
267 def);
David S. Millerf03b8a52006-02-15 00:35:50 -0800268
269 def = 32;
David S. Miller80dc0d62005-09-26 00:32:17 -0700270 cpu_data(0).icache_line_size =
David S. Miller07f8e5f2006-06-21 23:34:02 -0700271 of_getintprop_default(dp, "icache-line-size", def);
David S. Millerf03b8a52006-02-15 00:35:50 -0800272
273 def = ((tlb_type == hypervisor) ?
274 (3 * 1024 * 1024) :
275 (4 * 1024 * 1024));
David S. Miller07f8e5f2006-06-21 23:34:02 -0700276 cpu_data(0).ecache_size = of_getintprop_default(dp,
277 "ecache-size",
278 def);
David S. Millerf03b8a52006-02-15 00:35:50 -0800279
280 def = 64;
David S. Miller80dc0d62005-09-26 00:32:17 -0700281 cpu_data(0).ecache_line_size =
David S. Miller07f8e5f2006-06-21 23:34:02 -0700282 of_getintprop_default(dp, "ecache-line-size", def);
David S. Miller80dc0d62005-09-26 00:32:17 -0700283 printk("CPU[0]: Caches "
284 "D[sz(%d):line_sz(%d)] "
285 "I[sz(%d):line_sz(%d)] "
286 "E[sz(%d):line_sz(%d)]\n",
287 cpu_data(0).dcache_size, cpu_data(0).dcache_line_size,
288 cpu_data(0).icache_size, cpu_data(0).icache_line_size,
289 cpu_data(0).ecache_size, cpu_data(0).ecache_line_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
291#endif
292
David S. Millere77227e2006-02-13 20:42:16 -0800293 sun4v_virtual_device_probe();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 central_probe();
295
296 cpu_probe();
297}