Commit 920c3ed741340a88f2042ab0c44a25b8c743a379
1 parent
cb32da0416
Exists in
master
and in
7 other branches
[SPARC64]: Add basic infrastructure for MD add/remove notification.
And add dummy handlers for the VIO device layer. These will be filled in with real code after the vdc, vnet, and ds drivers are reworked to have simpler dependencies on the VIO device tree. Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 118 additions and 3 deletions Inline Diff
arch/sparc64/kernel/mdesc.c
1 | /* mdesc.c: Sun4V machine description handling. | 1 | /* mdesc.c: Sun4V machine description handling. |
2 | * | 2 | * |
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> |
4 | */ | 4 | */ |
5 | #include <linux/kernel.h> | 5 | #include <linux/kernel.h> |
6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
7 | #include <linux/bootmem.h> | 7 | #include <linux/bootmem.h> |
8 | #include <linux/log2.h> | 8 | #include <linux/log2.h> |
9 | #include <linux/list.h> | 9 | #include <linux/list.h> |
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
12 | 12 | ||
13 | #include <asm/hypervisor.h> | 13 | #include <asm/hypervisor.h> |
14 | #include <asm/mdesc.h> | 14 | #include <asm/mdesc.h> |
15 | #include <asm/prom.h> | 15 | #include <asm/prom.h> |
16 | #include <asm/oplib.h> | 16 | #include <asm/oplib.h> |
17 | #include <asm/smp.h> | 17 | #include <asm/smp.h> |
18 | 18 | ||
19 | /* Unlike the OBP device tree, the machine description is a full-on | 19 | /* Unlike the OBP device tree, the machine description is a full-on |
20 | * DAG. An arbitrary number of ARCs are possible from one | 20 | * DAG. An arbitrary number of ARCs are possible from one |
21 | * node to other nodes and thus we can't use the OBP device_node | 21 | * node to other nodes and thus we can't use the OBP device_node |
22 | * data structure to represent these nodes inside of the kernel. | 22 | * data structure to represent these nodes inside of the kernel. |
23 | * | 23 | * |
24 | * Actually, it isn't even a DAG, because there are back pointers | 24 | * Actually, it isn't even a DAG, because there are back pointers |
25 | * which create cycles in the graph. | 25 | * which create cycles in the graph. |
26 | * | 26 | * |
27 | * mdesc_hdr and mdesc_elem describe the layout of the data structure | 27 | * mdesc_hdr and mdesc_elem describe the layout of the data structure |
28 | * we get from the Hypervisor. | 28 | * we get from the Hypervisor. |
29 | */ | 29 | */ |
30 | struct mdesc_hdr { | 30 | struct mdesc_hdr { |
31 | u32 version; /* Transport version */ | 31 | u32 version; /* Transport version */ |
32 | u32 node_sz; /* node block size */ | 32 | u32 node_sz; /* node block size */ |
33 | u32 name_sz; /* name block size */ | 33 | u32 name_sz; /* name block size */ |
34 | u32 data_sz; /* data block size */ | 34 | u32 data_sz; /* data block size */ |
35 | } __attribute__((aligned(16))); | 35 | } __attribute__((aligned(16))); |
36 | 36 | ||
37 | struct mdesc_elem { | 37 | struct mdesc_elem { |
38 | u8 tag; | 38 | u8 tag; |
39 | #define MD_LIST_END 0x00 | 39 | #define MD_LIST_END 0x00 |
40 | #define MD_NODE 0x4e | 40 | #define MD_NODE 0x4e |
41 | #define MD_NODE_END 0x45 | 41 | #define MD_NODE_END 0x45 |
42 | #define MD_NOOP 0x20 | 42 | #define MD_NOOP 0x20 |
43 | #define MD_PROP_ARC 0x61 | 43 | #define MD_PROP_ARC 0x61 |
44 | #define MD_PROP_VAL 0x76 | 44 | #define MD_PROP_VAL 0x76 |
45 | #define MD_PROP_STR 0x73 | 45 | #define MD_PROP_STR 0x73 |
46 | #define MD_PROP_DATA 0x64 | 46 | #define MD_PROP_DATA 0x64 |
47 | u8 name_len; | 47 | u8 name_len; |
48 | u16 resv; | 48 | u16 resv; |
49 | u32 name_offset; | 49 | u32 name_offset; |
50 | union { | 50 | union { |
51 | struct { | 51 | struct { |
52 | u32 data_len; | 52 | u32 data_len; |
53 | u32 data_offset; | 53 | u32 data_offset; |
54 | } data; | 54 | } data; |
55 | u64 val; | 55 | u64 val; |
56 | } d; | 56 | } d; |
57 | }; | 57 | }; |
58 | 58 | ||
59 | struct mdesc_mem_ops { | 59 | struct mdesc_mem_ops { |
60 | struct mdesc_handle *(*alloc)(unsigned int mdesc_size); | 60 | struct mdesc_handle *(*alloc)(unsigned int mdesc_size); |
61 | void (*free)(struct mdesc_handle *handle); | 61 | void (*free)(struct mdesc_handle *handle); |
62 | }; | 62 | }; |
63 | 63 | ||
64 | struct mdesc_handle { | 64 | struct mdesc_handle { |
65 | struct list_head list; | 65 | struct list_head list; |
66 | struct mdesc_mem_ops *mops; | 66 | struct mdesc_mem_ops *mops; |
67 | void *self_base; | 67 | void *self_base; |
68 | atomic_t refcnt; | 68 | atomic_t refcnt; |
69 | unsigned int handle_size; | 69 | unsigned int handle_size; |
70 | struct mdesc_hdr mdesc; | 70 | struct mdesc_hdr mdesc; |
71 | }; | 71 | }; |
72 | 72 | ||
73 | static void mdesc_handle_init(struct mdesc_handle *hp, | 73 | static void mdesc_handle_init(struct mdesc_handle *hp, |
74 | unsigned int handle_size, | 74 | unsigned int handle_size, |
75 | void *base) | 75 | void *base) |
76 | { | 76 | { |
77 | BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1)); | 77 | BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1)); |
78 | 78 | ||
79 | memset(hp, 0, handle_size); | 79 | memset(hp, 0, handle_size); |
80 | INIT_LIST_HEAD(&hp->list); | 80 | INIT_LIST_HEAD(&hp->list); |
81 | hp->self_base = base; | 81 | hp->self_base = base; |
82 | atomic_set(&hp->refcnt, 1); | 82 | atomic_set(&hp->refcnt, 1); |
83 | hp->handle_size = handle_size; | 83 | hp->handle_size = handle_size; |
84 | } | 84 | } |
85 | 85 | ||
86 | static struct mdesc_handle *mdesc_bootmem_alloc(unsigned int mdesc_size) | 86 | static struct mdesc_handle *mdesc_bootmem_alloc(unsigned int mdesc_size) |
87 | { | 87 | { |
88 | struct mdesc_handle *hp; | 88 | struct mdesc_handle *hp; |
89 | unsigned int handle_size, alloc_size; | 89 | unsigned int handle_size, alloc_size; |
90 | 90 | ||
91 | handle_size = (sizeof(struct mdesc_handle) - | 91 | handle_size = (sizeof(struct mdesc_handle) - |
92 | sizeof(struct mdesc_hdr) + | 92 | sizeof(struct mdesc_hdr) + |
93 | mdesc_size); | 93 | mdesc_size); |
94 | alloc_size = PAGE_ALIGN(handle_size); | 94 | alloc_size = PAGE_ALIGN(handle_size); |
95 | 95 | ||
96 | hp = __alloc_bootmem(alloc_size, PAGE_SIZE, 0UL); | 96 | hp = __alloc_bootmem(alloc_size, PAGE_SIZE, 0UL); |
97 | if (hp) | 97 | if (hp) |
98 | mdesc_handle_init(hp, handle_size, hp); | 98 | mdesc_handle_init(hp, handle_size, hp); |
99 | 99 | ||
100 | return hp; | 100 | return hp; |
101 | } | 101 | } |
102 | 102 | ||
103 | static void mdesc_bootmem_free(struct mdesc_handle *hp) | 103 | static void mdesc_bootmem_free(struct mdesc_handle *hp) |
104 | { | 104 | { |
105 | unsigned int alloc_size, handle_size = hp->handle_size; | 105 | unsigned int alloc_size, handle_size = hp->handle_size; |
106 | unsigned long start, end; | 106 | unsigned long start, end; |
107 | 107 | ||
108 | BUG_ON(atomic_read(&hp->refcnt) != 0); | 108 | BUG_ON(atomic_read(&hp->refcnt) != 0); |
109 | BUG_ON(!list_empty(&hp->list)); | 109 | BUG_ON(!list_empty(&hp->list)); |
110 | 110 | ||
111 | alloc_size = PAGE_ALIGN(handle_size); | 111 | alloc_size = PAGE_ALIGN(handle_size); |
112 | 112 | ||
113 | start = (unsigned long) hp; | 113 | start = (unsigned long) hp; |
114 | end = start + alloc_size; | 114 | end = start + alloc_size; |
115 | 115 | ||
116 | while (start < end) { | 116 | while (start < end) { |
117 | struct page *p; | 117 | struct page *p; |
118 | 118 | ||
119 | p = virt_to_page(start); | 119 | p = virt_to_page(start); |
120 | ClearPageReserved(p); | 120 | ClearPageReserved(p); |
121 | __free_page(p); | 121 | __free_page(p); |
122 | start += PAGE_SIZE; | 122 | start += PAGE_SIZE; |
123 | } | 123 | } |
124 | } | 124 | } |
125 | 125 | ||
126 | static struct mdesc_mem_ops bootmem_mdesc_memops = { | 126 | static struct mdesc_mem_ops bootmem_mdesc_memops = { |
127 | .alloc = mdesc_bootmem_alloc, | 127 | .alloc = mdesc_bootmem_alloc, |
128 | .free = mdesc_bootmem_free, | 128 | .free = mdesc_bootmem_free, |
129 | }; | 129 | }; |
130 | 130 | ||
131 | static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) | 131 | static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) |
132 | { | 132 | { |
133 | unsigned int handle_size; | 133 | unsigned int handle_size; |
134 | void *base; | 134 | void *base; |
135 | 135 | ||
136 | handle_size = (sizeof(struct mdesc_handle) - | 136 | handle_size = (sizeof(struct mdesc_handle) - |
137 | sizeof(struct mdesc_hdr) + | 137 | sizeof(struct mdesc_hdr) + |
138 | mdesc_size); | 138 | mdesc_size); |
139 | 139 | ||
140 | base = kmalloc(handle_size + 15, GFP_KERNEL); | 140 | base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); |
141 | if (base) { | 141 | if (base) { |
142 | struct mdesc_handle *hp; | 142 | struct mdesc_handle *hp; |
143 | unsigned long addr; | 143 | unsigned long addr; |
144 | 144 | ||
145 | addr = (unsigned long)base; | 145 | addr = (unsigned long)base; |
146 | addr = (addr + 15UL) & ~15UL; | 146 | addr = (addr + 15UL) & ~15UL; |
147 | hp = (struct mdesc_handle *) addr; | 147 | hp = (struct mdesc_handle *) addr; |
148 | 148 | ||
149 | mdesc_handle_init(hp, handle_size, base); | 149 | mdesc_handle_init(hp, handle_size, base); |
150 | return hp; | 150 | return hp; |
151 | } | 151 | } |
152 | 152 | ||
153 | return NULL; | 153 | return NULL; |
154 | } | 154 | } |
155 | 155 | ||
156 | static void mdesc_kfree(struct mdesc_handle *hp) | 156 | static void mdesc_kfree(struct mdesc_handle *hp) |
157 | { | 157 | { |
158 | BUG_ON(atomic_read(&hp->refcnt) != 0); | 158 | BUG_ON(atomic_read(&hp->refcnt) != 0); |
159 | BUG_ON(!list_empty(&hp->list)); | 159 | BUG_ON(!list_empty(&hp->list)); |
160 | 160 | ||
161 | kfree(hp->self_base); | 161 | kfree(hp->self_base); |
162 | } | 162 | } |
163 | 163 | ||
164 | static struct mdesc_mem_ops kmalloc_mdesc_memops = { | 164 | static struct mdesc_mem_ops kmalloc_mdesc_memops = { |
165 | .alloc = mdesc_kmalloc, | 165 | .alloc = mdesc_kmalloc, |
166 | .free = mdesc_kfree, | 166 | .free = mdesc_kfree, |
167 | }; | 167 | }; |
168 | 168 | ||
169 | static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size, | 169 | static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size, |
170 | struct mdesc_mem_ops *mops) | 170 | struct mdesc_mem_ops *mops) |
171 | { | 171 | { |
172 | struct mdesc_handle *hp = mops->alloc(mdesc_size); | 172 | struct mdesc_handle *hp = mops->alloc(mdesc_size); |
173 | 173 | ||
174 | if (hp) | 174 | if (hp) |
175 | hp->mops = mops; | 175 | hp->mops = mops; |
176 | 176 | ||
177 | return hp; | 177 | return hp; |
178 | } | 178 | } |
179 | 179 | ||
180 | static void mdesc_free(struct mdesc_handle *hp) | 180 | static void mdesc_free(struct mdesc_handle *hp) |
181 | { | 181 | { |
182 | hp->mops->free(hp); | 182 | hp->mops->free(hp); |
183 | } | 183 | } |
184 | 184 | ||
185 | static struct mdesc_handle *cur_mdesc; | 185 | static struct mdesc_handle *cur_mdesc; |
186 | static LIST_HEAD(mdesc_zombie_list); | 186 | static LIST_HEAD(mdesc_zombie_list); |
187 | static DEFINE_SPINLOCK(mdesc_lock); | 187 | static DEFINE_SPINLOCK(mdesc_lock); |
188 | 188 | ||
189 | struct mdesc_handle *mdesc_grab(void) | 189 | struct mdesc_handle *mdesc_grab(void) |
190 | { | 190 | { |
191 | struct mdesc_handle *hp; | 191 | struct mdesc_handle *hp; |
192 | unsigned long flags; | 192 | unsigned long flags; |
193 | 193 | ||
194 | spin_lock_irqsave(&mdesc_lock, flags); | 194 | spin_lock_irqsave(&mdesc_lock, flags); |
195 | hp = cur_mdesc; | 195 | hp = cur_mdesc; |
196 | if (hp) | 196 | if (hp) |
197 | atomic_inc(&hp->refcnt); | 197 | atomic_inc(&hp->refcnt); |
198 | spin_unlock_irqrestore(&mdesc_lock, flags); | 198 | spin_unlock_irqrestore(&mdesc_lock, flags); |
199 | 199 | ||
200 | return hp; | 200 | return hp; |
201 | } | 201 | } |
202 | EXPORT_SYMBOL(mdesc_grab); | 202 | EXPORT_SYMBOL(mdesc_grab); |
203 | 203 | ||
204 | void mdesc_release(struct mdesc_handle *hp) | 204 | void mdesc_release(struct mdesc_handle *hp) |
205 | { | 205 | { |
206 | unsigned long flags; | 206 | unsigned long flags; |
207 | 207 | ||
208 | spin_lock_irqsave(&mdesc_lock, flags); | 208 | spin_lock_irqsave(&mdesc_lock, flags); |
209 | if (atomic_dec_and_test(&hp->refcnt)) { | 209 | if (atomic_dec_and_test(&hp->refcnt)) { |
210 | list_del_init(&hp->list); | 210 | list_del_init(&hp->list); |
211 | hp->mops->free(hp); | 211 | hp->mops->free(hp); |
212 | } | 212 | } |
213 | spin_unlock_irqrestore(&mdesc_lock, flags); | 213 | spin_unlock_irqrestore(&mdesc_lock, flags); |
214 | } | 214 | } |
215 | EXPORT_SYMBOL(mdesc_release); | 215 | EXPORT_SYMBOL(mdesc_release); |
216 | 216 | ||
217 | static DEFINE_MUTEX(mdesc_mutex); | ||
218 | static struct mdesc_notifier_client *client_list; | ||
219 | |||
220 | void mdesc_register_notifier(struct mdesc_notifier_client *client) | ||
221 | { | ||
222 | u64 node; | ||
223 | |||
224 | mutex_lock(&mdesc_mutex); | ||
225 | client->next = client_list; | ||
226 | client_list = client; | ||
227 | |||
228 | mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) | ||
229 | client->add(cur_mdesc, node); | ||
230 | |||
231 | mutex_unlock(&mdesc_mutex); | ||
232 | } | ||
233 | |||
234 | /* Run 'func' on nodes which are in A but not in B. */ | ||
235 | static void invoke_on_missing(const char *name, | ||
236 | struct mdesc_handle *a, | ||
237 | struct mdesc_handle *b, | ||
238 | void (*func)(struct mdesc_handle *, u64)) | ||
239 | { | ||
240 | u64 node; | ||
241 | |||
242 | mdesc_for_each_node_by_name(a, node, name) { | ||
243 | const u64 *id = mdesc_get_property(a, node, "id", NULL); | ||
244 | int found = 0; | ||
245 | u64 fnode; | ||
246 | |||
247 | mdesc_for_each_node_by_name(b, fnode, name) { | ||
248 | const u64 *fid = mdesc_get_property(b, fnode, | ||
249 | "id", NULL); | ||
250 | |||
251 | if (*id == *fid) { | ||
252 | found = 1; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | if (!found) | ||
257 | func(a, node); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static void notify_one(struct mdesc_notifier_client *p, | ||
262 | struct mdesc_handle *old_hp, | ||
263 | struct mdesc_handle *new_hp) | ||
264 | { | ||
265 | invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); | ||
266 | invoke_on_missing(p->node_name, new_hp, old_hp, p->add); | ||
267 | } | ||
268 | |||
269 | static void mdesc_notify_clients(struct mdesc_handle *old_hp, | ||
270 | struct mdesc_handle *new_hp) | ||
271 | { | ||
272 | struct mdesc_notifier_client *p = client_list; | ||
273 | |||
274 | while (p) { | ||
275 | notify_one(p, old_hp, new_hp); | ||
276 | p = p->next; | ||
277 | } | ||
278 | } | ||
279 | |||
217 | void mdesc_update(void) | 280 | void mdesc_update(void) |
218 | { | 281 | { |
219 | unsigned long len, real_len, status; | 282 | unsigned long len, real_len, status; |
220 | struct mdesc_handle *hp, *orig_hp; | 283 | struct mdesc_handle *hp, *orig_hp; |
221 | unsigned long flags; | 284 | unsigned long flags; |
222 | 285 | ||
286 | mutex_lock(&mdesc_mutex); | ||
287 | |||
223 | (void) sun4v_mach_desc(0UL, 0UL, &len); | 288 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
224 | 289 | ||
225 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); | 290 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); |
226 | if (!hp) { | 291 | if (!hp) { |
227 | printk(KERN_ERR "MD: mdesc alloc fails\n"); | 292 | printk(KERN_ERR "MD: mdesc alloc fails\n"); |
228 | return; | 293 | goto out; |
229 | } | 294 | } |
230 | 295 | ||
231 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | 296 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
232 | if (status != HV_EOK || real_len > len) { | 297 | if (status != HV_EOK || real_len > len) { |
233 | printk(KERN_ERR "MD: mdesc reread fails with %lu\n", | 298 | printk(KERN_ERR "MD: mdesc reread fails with %lu\n", |
234 | status); | 299 | status); |
235 | atomic_dec(&hp->refcnt); | 300 | atomic_dec(&hp->refcnt); |
236 | mdesc_free(hp); | 301 | mdesc_free(hp); |
237 | return; | 302 | goto out; |
238 | } | 303 | } |
239 | 304 | ||
240 | spin_lock_irqsave(&mdesc_lock, flags); | 305 | spin_lock_irqsave(&mdesc_lock, flags); |
241 | orig_hp = cur_mdesc; | 306 | orig_hp = cur_mdesc; |
242 | cur_mdesc = hp; | 307 | cur_mdesc = hp; |
308 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
243 | 309 | ||
310 | mdesc_notify_clients(orig_hp, hp); | ||
311 | |||
312 | spin_lock_irqsave(&mdesc_lock, flags); | ||
244 | if (atomic_dec_and_test(&orig_hp->refcnt)) | 313 | if (atomic_dec_and_test(&orig_hp->refcnt)) |
245 | mdesc_free(orig_hp); | 314 | mdesc_free(orig_hp); |
246 | else | 315 | else |
247 | list_add(&orig_hp->list, &mdesc_zombie_list); | 316 | list_add(&orig_hp->list, &mdesc_zombie_list); |
248 | spin_unlock_irqrestore(&mdesc_lock, flags); | 317 | spin_unlock_irqrestore(&mdesc_lock, flags); |
318 | |||
319 | out: | ||
320 | mutex_unlock(&mdesc_mutex); | ||
249 | } | 321 | } |
250 | 322 | ||
251 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | 323 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) |
252 | { | 324 | { |
253 | return (struct mdesc_elem *) (mdesc + 1); | 325 | return (struct mdesc_elem *) (mdesc + 1); |
254 | } | 326 | } |
255 | 327 | ||
256 | static void *name_block(struct mdesc_hdr *mdesc) | 328 | static void *name_block(struct mdesc_hdr *mdesc) |
257 | { | 329 | { |
258 | return ((void *) node_block(mdesc)) + mdesc->node_sz; | 330 | return ((void *) node_block(mdesc)) + mdesc->node_sz; |
259 | } | 331 | } |
260 | 332 | ||
261 | static void *data_block(struct mdesc_hdr *mdesc) | 333 | static void *data_block(struct mdesc_hdr *mdesc) |
262 | { | 334 | { |
263 | return ((void *) name_block(mdesc)) + mdesc->name_sz; | 335 | return ((void *) name_block(mdesc)) + mdesc->name_sz; |
264 | } | 336 | } |
265 | 337 | ||
266 | u64 mdesc_node_by_name(struct mdesc_handle *hp, | 338 | u64 mdesc_node_by_name(struct mdesc_handle *hp, |
267 | u64 from_node, const char *name) | 339 | u64 from_node, const char *name) |
268 | { | 340 | { |
269 | struct mdesc_elem *ep = node_block(&hp->mdesc); | 341 | struct mdesc_elem *ep = node_block(&hp->mdesc); |
270 | const char *names = name_block(&hp->mdesc); | 342 | const char *names = name_block(&hp->mdesc); |
271 | u64 last_node = hp->mdesc.node_sz / 16; | 343 | u64 last_node = hp->mdesc.node_sz / 16; |
272 | u64 ret; | 344 | u64 ret; |
273 | 345 | ||
274 | if (from_node == MDESC_NODE_NULL) { | 346 | if (from_node == MDESC_NODE_NULL) { |
275 | ret = from_node = 0; | 347 | ret = from_node = 0; |
276 | } else if (from_node >= last_node) { | 348 | } else if (from_node >= last_node) { |
277 | return MDESC_NODE_NULL; | 349 | return MDESC_NODE_NULL; |
278 | } else { | 350 | } else { |
279 | ret = ep[from_node].d.val; | 351 | ret = ep[from_node].d.val; |
280 | } | 352 | } |
281 | 353 | ||
282 | while (ret < last_node) { | 354 | while (ret < last_node) { |
283 | if (ep[ret].tag != MD_NODE) | 355 | if (ep[ret].tag != MD_NODE) |
284 | return MDESC_NODE_NULL; | 356 | return MDESC_NODE_NULL; |
285 | if (!strcmp(names + ep[ret].name_offset, name)) | 357 | if (!strcmp(names + ep[ret].name_offset, name)) |
286 | break; | 358 | break; |
287 | ret = ep[ret].d.val; | 359 | ret = ep[ret].d.val; |
288 | } | 360 | } |
289 | if (ret >= last_node) | 361 | if (ret >= last_node) |
290 | ret = MDESC_NODE_NULL; | 362 | ret = MDESC_NODE_NULL; |
291 | return ret; | 363 | return ret; |
292 | } | 364 | } |
293 | EXPORT_SYMBOL(mdesc_node_by_name); | 365 | EXPORT_SYMBOL(mdesc_node_by_name); |
294 | 366 | ||
295 | const void *mdesc_get_property(struct mdesc_handle *hp, u64 node, | 367 | const void *mdesc_get_property(struct mdesc_handle *hp, u64 node, |
296 | const char *name, int *lenp) | 368 | const char *name, int *lenp) |
297 | { | 369 | { |
298 | const char *names = name_block(&hp->mdesc); | 370 | const char *names = name_block(&hp->mdesc); |
299 | u64 last_node = hp->mdesc.node_sz / 16; | 371 | u64 last_node = hp->mdesc.node_sz / 16; |
300 | void *data = data_block(&hp->mdesc); | 372 | void *data = data_block(&hp->mdesc); |
301 | struct mdesc_elem *ep; | 373 | struct mdesc_elem *ep; |
302 | 374 | ||
303 | if (node == MDESC_NODE_NULL || node >= last_node) | 375 | if (node == MDESC_NODE_NULL || node >= last_node) |
304 | return NULL; | 376 | return NULL; |
305 | 377 | ||
306 | ep = node_block(&hp->mdesc) + node; | 378 | ep = node_block(&hp->mdesc) + node; |
307 | ep++; | 379 | ep++; |
308 | for (; ep->tag != MD_NODE_END; ep++) { | 380 | for (; ep->tag != MD_NODE_END; ep++) { |
309 | void *val = NULL; | 381 | void *val = NULL; |
310 | int len = 0; | 382 | int len = 0; |
311 | 383 | ||
312 | switch (ep->tag) { | 384 | switch (ep->tag) { |
313 | case MD_PROP_VAL: | 385 | case MD_PROP_VAL: |
314 | val = &ep->d.val; | 386 | val = &ep->d.val; |
315 | len = 8; | 387 | len = 8; |
316 | break; | 388 | break; |
317 | 389 | ||
318 | case MD_PROP_STR: | 390 | case MD_PROP_STR: |
319 | case MD_PROP_DATA: | 391 | case MD_PROP_DATA: |
320 | val = data + ep->d.data.data_offset; | 392 | val = data + ep->d.data.data_offset; |
321 | len = ep->d.data.data_len; | 393 | len = ep->d.data.data_len; |
322 | break; | 394 | break; |
323 | 395 | ||
324 | default: | 396 | default: |
325 | break; | 397 | break; |
326 | } | 398 | } |
327 | if (!val) | 399 | if (!val) |
328 | continue; | 400 | continue; |
329 | 401 | ||
330 | if (!strcmp(names + ep->name_offset, name)) { | 402 | if (!strcmp(names + ep->name_offset, name)) { |
331 | if (lenp) | 403 | if (lenp) |
332 | *lenp = len; | 404 | *lenp = len; |
333 | return val; | 405 | return val; |
334 | } | 406 | } |
335 | } | 407 | } |
336 | 408 | ||
337 | return NULL; | 409 | return NULL; |
338 | } | 410 | } |
339 | EXPORT_SYMBOL(mdesc_get_property); | 411 | EXPORT_SYMBOL(mdesc_get_property); |
340 | 412 | ||
341 | u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type) | 413 | u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type) |
342 | { | 414 | { |
343 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | 415 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
344 | const char *names = name_block(&hp->mdesc); | 416 | const char *names = name_block(&hp->mdesc); |
345 | u64 last_node = hp->mdesc.node_sz / 16; | 417 | u64 last_node = hp->mdesc.node_sz / 16; |
346 | 418 | ||
347 | if (from == MDESC_NODE_NULL || from >= last_node) | 419 | if (from == MDESC_NODE_NULL || from >= last_node) |
348 | return MDESC_NODE_NULL; | 420 | return MDESC_NODE_NULL; |
349 | 421 | ||
350 | ep = base + from; | 422 | ep = base + from; |
351 | 423 | ||
352 | ep++; | 424 | ep++; |
353 | for (; ep->tag != MD_NODE_END; ep++) { | 425 | for (; ep->tag != MD_NODE_END; ep++) { |
354 | if (ep->tag != MD_PROP_ARC) | 426 | if (ep->tag != MD_PROP_ARC) |
355 | continue; | 427 | continue; |
356 | 428 | ||
357 | if (strcmp(names + ep->name_offset, arc_type)) | 429 | if (strcmp(names + ep->name_offset, arc_type)) |
358 | continue; | 430 | continue; |
359 | 431 | ||
360 | return ep - base; | 432 | return ep - base; |
361 | } | 433 | } |
362 | 434 | ||
363 | return MDESC_NODE_NULL; | 435 | return MDESC_NODE_NULL; |
364 | } | 436 | } |
365 | EXPORT_SYMBOL(mdesc_next_arc); | 437 | EXPORT_SYMBOL(mdesc_next_arc); |
366 | 438 | ||
367 | u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc) | 439 | u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc) |
368 | { | 440 | { |
369 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | 441 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
370 | 442 | ||
371 | ep = base + arc; | 443 | ep = base + arc; |
372 | 444 | ||
373 | return ep->d.val; | 445 | return ep->d.val; |
374 | } | 446 | } |
375 | EXPORT_SYMBOL(mdesc_arc_target); | 447 | EXPORT_SYMBOL(mdesc_arc_target); |
376 | 448 | ||
377 | const char *mdesc_node_name(struct mdesc_handle *hp, u64 node) | 449 | const char *mdesc_node_name(struct mdesc_handle *hp, u64 node) |
378 | { | 450 | { |
379 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); | 451 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
380 | const char *names = name_block(&hp->mdesc); | 452 | const char *names = name_block(&hp->mdesc); |
381 | u64 last_node = hp->mdesc.node_sz / 16; | 453 | u64 last_node = hp->mdesc.node_sz / 16; |
382 | 454 | ||
383 | if (node == MDESC_NODE_NULL || node >= last_node) | 455 | if (node == MDESC_NODE_NULL || node >= last_node) |
384 | return NULL; | 456 | return NULL; |
385 | 457 | ||
386 | ep = base + node; | 458 | ep = base + node; |
387 | if (ep->tag != MD_NODE) | 459 | if (ep->tag != MD_NODE) |
388 | return NULL; | 460 | return NULL; |
389 | 461 | ||
390 | return names + ep->name_offset; | 462 | return names + ep->name_offset; |
391 | } | 463 | } |
392 | EXPORT_SYMBOL(mdesc_node_name); | 464 | EXPORT_SYMBOL(mdesc_node_name); |
393 | 465 | ||
394 | static void __init report_platform_properties(void) | 466 | static void __init report_platform_properties(void) |
395 | { | 467 | { |
396 | struct mdesc_handle *hp = mdesc_grab(); | 468 | struct mdesc_handle *hp = mdesc_grab(); |
397 | u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); | 469 | u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); |
398 | const char *s; | 470 | const char *s; |
399 | const u64 *v; | 471 | const u64 *v; |
400 | 472 | ||
401 | if (pn == MDESC_NODE_NULL) { | 473 | if (pn == MDESC_NODE_NULL) { |
402 | prom_printf("No platform node in machine-description.\n"); | 474 | prom_printf("No platform node in machine-description.\n"); |
403 | prom_halt(); | 475 | prom_halt(); |
404 | } | 476 | } |
405 | 477 | ||
406 | s = mdesc_get_property(hp, pn, "banner-name", NULL); | 478 | s = mdesc_get_property(hp, pn, "banner-name", NULL); |
407 | printk("PLATFORM: banner-name [%s]\n", s); | 479 | printk("PLATFORM: banner-name [%s]\n", s); |
408 | s = mdesc_get_property(hp, pn, "name", NULL); | 480 | s = mdesc_get_property(hp, pn, "name", NULL); |
409 | printk("PLATFORM: name [%s]\n", s); | 481 | printk("PLATFORM: name [%s]\n", s); |
410 | 482 | ||
411 | v = mdesc_get_property(hp, pn, "hostid", NULL); | 483 | v = mdesc_get_property(hp, pn, "hostid", NULL); |
412 | if (v) | 484 | if (v) |
413 | printk("PLATFORM: hostid [%08lx]\n", *v); | 485 | printk("PLATFORM: hostid [%08lx]\n", *v); |
414 | v = mdesc_get_property(hp, pn, "serial#", NULL); | 486 | v = mdesc_get_property(hp, pn, "serial#", NULL); |
415 | if (v) | 487 | if (v) |
416 | printk("PLATFORM: serial# [%08lx]\n", *v); | 488 | printk("PLATFORM: serial# [%08lx]\n", *v); |
417 | v = mdesc_get_property(hp, pn, "stick-frequency", NULL); | 489 | v = mdesc_get_property(hp, pn, "stick-frequency", NULL); |
418 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); | 490 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); |
419 | v = mdesc_get_property(hp, pn, "mac-address", NULL); | 491 | v = mdesc_get_property(hp, pn, "mac-address", NULL); |
420 | if (v) | 492 | if (v) |
421 | printk("PLATFORM: mac-address [%lx]\n", *v); | 493 | printk("PLATFORM: mac-address [%lx]\n", *v); |
422 | v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL); | 494 | v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL); |
423 | if (v) | 495 | if (v) |
424 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); | 496 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); |
425 | v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL); | 497 | v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL); |
426 | if (v) | 498 | if (v) |
427 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); | 499 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); |
428 | v = mdesc_get_property(hp, pn, "max-cpus", NULL); | 500 | v = mdesc_get_property(hp, pn, "max-cpus", NULL); |
429 | if (v) | 501 | if (v) |
430 | printk("PLATFORM: max-cpus [%lu]\n", *v); | 502 | printk("PLATFORM: max-cpus [%lu]\n", *v); |
431 | 503 | ||
432 | #ifdef CONFIG_SMP | 504 | #ifdef CONFIG_SMP |
433 | { | 505 | { |
434 | int max_cpu, i; | 506 | int max_cpu, i; |
435 | 507 | ||
436 | if (v) { | 508 | if (v) { |
437 | max_cpu = *v; | 509 | max_cpu = *v; |
438 | if (max_cpu > NR_CPUS) | 510 | if (max_cpu > NR_CPUS) |
439 | max_cpu = NR_CPUS; | 511 | max_cpu = NR_CPUS; |
440 | } else { | 512 | } else { |
441 | max_cpu = NR_CPUS; | 513 | max_cpu = NR_CPUS; |
442 | } | 514 | } |
443 | for (i = 0; i < max_cpu; i++) | 515 | for (i = 0; i < max_cpu; i++) |
444 | cpu_set(i, cpu_possible_map); | 516 | cpu_set(i, cpu_possible_map); |
445 | } | 517 | } |
446 | #endif | 518 | #endif |
447 | 519 | ||
448 | mdesc_release(hp); | 520 | mdesc_release(hp); |
449 | } | 521 | } |
450 | 522 | ||
451 | static int inline find_in_proplist(const char *list, const char *match, int len) | 523 | static int inline find_in_proplist(const char *list, const char *match, int len) |
452 | { | 524 | { |
453 | while (len > 0) { | 525 | while (len > 0) { |
454 | int l; | 526 | int l; |
455 | 527 | ||
456 | if (!strcmp(list, match)) | 528 | if (!strcmp(list, match)) |
457 | return 1; | 529 | return 1; |
458 | l = strlen(list) + 1; | 530 | l = strlen(list) + 1; |
459 | list += l; | 531 | list += l; |
460 | len -= l; | 532 | len -= l; |
461 | } | 533 | } |
462 | return 0; | 534 | return 0; |
463 | } | 535 | } |
464 | 536 | ||
465 | static void __devinit fill_in_one_cache(cpuinfo_sparc *c, | 537 | static void __devinit fill_in_one_cache(cpuinfo_sparc *c, |
466 | struct mdesc_handle *hp, | 538 | struct mdesc_handle *hp, |
467 | u64 mp) | 539 | u64 mp) |
468 | { | 540 | { |
469 | const u64 *level = mdesc_get_property(hp, mp, "level", NULL); | 541 | const u64 *level = mdesc_get_property(hp, mp, "level", NULL); |
470 | const u64 *size = mdesc_get_property(hp, mp, "size", NULL); | 542 | const u64 *size = mdesc_get_property(hp, mp, "size", NULL); |
471 | const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL); | 543 | const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL); |
472 | const char *type; | 544 | const char *type; |
473 | int type_len; | 545 | int type_len; |
474 | 546 | ||
475 | type = mdesc_get_property(hp, mp, "type", &type_len); | 547 | type = mdesc_get_property(hp, mp, "type", &type_len); |
476 | 548 | ||
477 | switch (*level) { | 549 | switch (*level) { |
478 | case 1: | 550 | case 1: |
479 | if (find_in_proplist(type, "instn", type_len)) { | 551 | if (find_in_proplist(type, "instn", type_len)) { |
480 | c->icache_size = *size; | 552 | c->icache_size = *size; |
481 | c->icache_line_size = *line_size; | 553 | c->icache_line_size = *line_size; |
482 | } else if (find_in_proplist(type, "data", type_len)) { | 554 | } else if (find_in_proplist(type, "data", type_len)) { |
483 | c->dcache_size = *size; | 555 | c->dcache_size = *size; |
484 | c->dcache_line_size = *line_size; | 556 | c->dcache_line_size = *line_size; |
485 | } | 557 | } |
486 | break; | 558 | break; |
487 | 559 | ||
488 | case 2: | 560 | case 2: |
489 | c->ecache_size = *size; | 561 | c->ecache_size = *size; |
490 | c->ecache_line_size = *line_size; | 562 | c->ecache_line_size = *line_size; |
491 | break; | 563 | break; |
492 | 564 | ||
493 | default: | 565 | default: |
494 | break; | 566 | break; |
495 | } | 567 | } |
496 | 568 | ||
497 | if (*level == 1) { | 569 | if (*level == 1) { |
498 | u64 a; | 570 | u64 a; |
499 | 571 | ||
500 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | 572 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
501 | u64 target = mdesc_arc_target(hp, a); | 573 | u64 target = mdesc_arc_target(hp, a); |
502 | const char *name = mdesc_node_name(hp, target); | 574 | const char *name = mdesc_node_name(hp, target); |
503 | 575 | ||
504 | if (!strcmp(name, "cache")) | 576 | if (!strcmp(name, "cache")) |
505 | fill_in_one_cache(c, hp, target); | 577 | fill_in_one_cache(c, hp, target); |
506 | } | 578 | } |
507 | } | 579 | } |
508 | } | 580 | } |
509 | 581 | ||
510 | static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp, | 582 | static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp, |
511 | int core_id) | 583 | int core_id) |
512 | { | 584 | { |
513 | u64 a; | 585 | u64 a; |
514 | 586 | ||
515 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { | 587 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { |
516 | u64 t = mdesc_arc_target(hp, a); | 588 | u64 t = mdesc_arc_target(hp, a); |
517 | const char *name; | 589 | const char *name; |
518 | const u64 *id; | 590 | const u64 *id; |
519 | 591 | ||
520 | name = mdesc_node_name(hp, t); | 592 | name = mdesc_node_name(hp, t); |
521 | if (!strcmp(name, "cpu")) { | 593 | if (!strcmp(name, "cpu")) { |
522 | id = mdesc_get_property(hp, t, "id", NULL); | 594 | id = mdesc_get_property(hp, t, "id", NULL); |
523 | if (*id < NR_CPUS) | 595 | if (*id < NR_CPUS) |
524 | cpu_data(*id).core_id = core_id; | 596 | cpu_data(*id).core_id = core_id; |
525 | } else { | 597 | } else { |
526 | u64 j; | 598 | u64 j; |
527 | 599 | ||
528 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) { | 600 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) { |
529 | u64 n = mdesc_arc_target(hp, j); | 601 | u64 n = mdesc_arc_target(hp, j); |
530 | const char *n_name; | 602 | const char *n_name; |
531 | 603 | ||
532 | n_name = mdesc_node_name(hp, n); | 604 | n_name = mdesc_node_name(hp, n); |
533 | if (strcmp(n_name, "cpu")) | 605 | if (strcmp(n_name, "cpu")) |
534 | continue; | 606 | continue; |
535 | 607 | ||
536 | id = mdesc_get_property(hp, n, "id", NULL); | 608 | id = mdesc_get_property(hp, n, "id", NULL); |
537 | if (*id < NR_CPUS) | 609 | if (*id < NR_CPUS) |
538 | cpu_data(*id).core_id = core_id; | 610 | cpu_data(*id).core_id = core_id; |
539 | } | 611 | } |
540 | } | 612 | } |
541 | } | 613 | } |
542 | } | 614 | } |
543 | 615 | ||
544 | static void __devinit set_core_ids(struct mdesc_handle *hp) | 616 | static void __devinit set_core_ids(struct mdesc_handle *hp) |
545 | { | 617 | { |
546 | int idx; | 618 | int idx; |
547 | u64 mp; | 619 | u64 mp; |
548 | 620 | ||
549 | idx = 1; | 621 | idx = 1; |
550 | mdesc_for_each_node_by_name(hp, mp, "cache") { | 622 | mdesc_for_each_node_by_name(hp, mp, "cache") { |
551 | const u64 *level; | 623 | const u64 *level; |
552 | const char *type; | 624 | const char *type; |
553 | int len; | 625 | int len; |
554 | 626 | ||
555 | level = mdesc_get_property(hp, mp, "level", NULL); | 627 | level = mdesc_get_property(hp, mp, "level", NULL); |
556 | if (*level != 1) | 628 | if (*level != 1) |
557 | continue; | 629 | continue; |
558 | 630 | ||
559 | type = mdesc_get_property(hp, mp, "type", &len); | 631 | type = mdesc_get_property(hp, mp, "type", &len); |
560 | if (!find_in_proplist(type, "instn", len)) | 632 | if (!find_in_proplist(type, "instn", len)) |
561 | continue; | 633 | continue; |
562 | 634 | ||
563 | mark_core_ids(hp, mp, idx); | 635 | mark_core_ids(hp, mp, idx); |
564 | 636 | ||
565 | idx++; | 637 | idx++; |
566 | } | 638 | } |
567 | } | 639 | } |
568 | 640 | ||
569 | static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp, | 641 | static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp, |
570 | int proc_id) | 642 | int proc_id) |
571 | { | 643 | { |
572 | u64 a; | 644 | u64 a; |
573 | 645 | ||
574 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { | 646 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { |
575 | u64 t = mdesc_arc_target(hp, a); | 647 | u64 t = mdesc_arc_target(hp, a); |
576 | const char *name; | 648 | const char *name; |
577 | const u64 *id; | 649 | const u64 *id; |
578 | 650 | ||
579 | name = mdesc_node_name(hp, t); | 651 | name = mdesc_node_name(hp, t); |
580 | if (strcmp(name, "cpu")) | 652 | if (strcmp(name, "cpu")) |
581 | continue; | 653 | continue; |
582 | 654 | ||
583 | id = mdesc_get_property(hp, t, "id", NULL); | 655 | id = mdesc_get_property(hp, t, "id", NULL); |
584 | if (*id < NR_CPUS) | 656 | if (*id < NR_CPUS) |
585 | cpu_data(*id).proc_id = proc_id; | 657 | cpu_data(*id).proc_id = proc_id; |
586 | } | 658 | } |
587 | } | 659 | } |
588 | 660 | ||
589 | static void __devinit __set_proc_ids(struct mdesc_handle *hp, | 661 | static void __devinit __set_proc_ids(struct mdesc_handle *hp, |
590 | const char *exec_unit_name) | 662 | const char *exec_unit_name) |
591 | { | 663 | { |
592 | int idx; | 664 | int idx; |
593 | u64 mp; | 665 | u64 mp; |
594 | 666 | ||
595 | idx = 0; | 667 | idx = 0; |
596 | mdesc_for_each_node_by_name(hp, mp, exec_unit_name) { | 668 | mdesc_for_each_node_by_name(hp, mp, exec_unit_name) { |
597 | const char *type; | 669 | const char *type; |
598 | int len; | 670 | int len; |
599 | 671 | ||
600 | type = mdesc_get_property(hp, mp, "type", &len); | 672 | type = mdesc_get_property(hp, mp, "type", &len); |
601 | if (!find_in_proplist(type, "int", len) && | 673 | if (!find_in_proplist(type, "int", len) && |
602 | !find_in_proplist(type, "integer", len)) | 674 | !find_in_proplist(type, "integer", len)) |
603 | continue; | 675 | continue; |
604 | 676 | ||
605 | mark_proc_ids(hp, mp, idx); | 677 | mark_proc_ids(hp, mp, idx); |
606 | 678 | ||
607 | idx++; | 679 | idx++; |
608 | } | 680 | } |
609 | } | 681 | } |
610 | 682 | ||
611 | static void __devinit set_proc_ids(struct mdesc_handle *hp) | 683 | static void __devinit set_proc_ids(struct mdesc_handle *hp) |
612 | { | 684 | { |
613 | __set_proc_ids(hp, "exec_unit"); | 685 | __set_proc_ids(hp, "exec_unit"); |
614 | __set_proc_ids(hp, "exec-unit"); | 686 | __set_proc_ids(hp, "exec-unit"); |
615 | } | 687 | } |
616 | 688 | ||
617 | static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask, | 689 | static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask, |
618 | unsigned char def) | 690 | unsigned char def) |
619 | { | 691 | { |
620 | u64 val; | 692 | u64 val; |
621 | 693 | ||
622 | if (!p) | 694 | if (!p) |
623 | goto use_default; | 695 | goto use_default; |
624 | val = *p; | 696 | val = *p; |
625 | 697 | ||
626 | if (!val || val >= 64) | 698 | if (!val || val >= 64) |
627 | goto use_default; | 699 | goto use_default; |
628 | 700 | ||
629 | *mask = ((1U << val) * 64U) - 1U; | 701 | *mask = ((1U << val) * 64U) - 1U; |
630 | return; | 702 | return; |
631 | 703 | ||
632 | use_default: | 704 | use_default: |
633 | *mask = ((1U << def) * 64U) - 1U; | 705 | *mask = ((1U << def) * 64U) - 1U; |
634 | } | 706 | } |
635 | 707 | ||
636 | static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp, | 708 | static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp, |
637 | struct trap_per_cpu *tb) | 709 | struct trap_per_cpu *tb) |
638 | { | 710 | { |
639 | const u64 *val; | 711 | const u64 *val; |
640 | 712 | ||
641 | val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL); | 713 | val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL); |
642 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); | 714 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); |
643 | 715 | ||
644 | val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL); | 716 | val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL); |
645 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); | 717 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); |
646 | 718 | ||
647 | val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL); | 719 | val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL); |
648 | get_one_mondo_bits(val, &tb->resum_qmask, 6); | 720 | get_one_mondo_bits(val, &tb->resum_qmask, 6); |
649 | 721 | ||
650 | val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL); | 722 | val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL); |
651 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); | 723 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); |
652 | } | 724 | } |
653 | 725 | ||
654 | void __devinit mdesc_fill_in_cpu_data(cpumask_t mask) | 726 | void __devinit mdesc_fill_in_cpu_data(cpumask_t mask) |
655 | { | 727 | { |
656 | struct mdesc_handle *hp = mdesc_grab(); | 728 | struct mdesc_handle *hp = mdesc_grab(); |
657 | u64 mp; | 729 | u64 mp; |
658 | 730 | ||
659 | ncpus_probed = 0; | 731 | ncpus_probed = 0; |
660 | mdesc_for_each_node_by_name(hp, mp, "cpu") { | 732 | mdesc_for_each_node_by_name(hp, mp, "cpu") { |
661 | const u64 *id = mdesc_get_property(hp, mp, "id", NULL); | 733 | const u64 *id = mdesc_get_property(hp, mp, "id", NULL); |
662 | const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL); | 734 | const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL); |
663 | struct trap_per_cpu *tb; | 735 | struct trap_per_cpu *tb; |
664 | cpuinfo_sparc *c; | 736 | cpuinfo_sparc *c; |
665 | int cpuid; | 737 | int cpuid; |
666 | u64 a; | 738 | u64 a; |
667 | 739 | ||
668 | ncpus_probed++; | 740 | ncpus_probed++; |
669 | 741 | ||
670 | cpuid = *id; | 742 | cpuid = *id; |
671 | 743 | ||
672 | #ifdef CONFIG_SMP | 744 | #ifdef CONFIG_SMP |
673 | if (cpuid >= NR_CPUS) | 745 | if (cpuid >= NR_CPUS) |
674 | continue; | 746 | continue; |
675 | if (!cpu_isset(cpuid, mask)) | 747 | if (!cpu_isset(cpuid, mask)) |
676 | continue; | 748 | continue; |
677 | #else | 749 | #else |
678 | /* On uniprocessor we only want the values for the | 750 | /* On uniprocessor we only want the values for the |
679 | * real physical cpu the kernel booted onto, however | 751 | * real physical cpu the kernel booted onto, however |
680 | * cpu_data() only has one entry at index 0. | 752 | * cpu_data() only has one entry at index 0. |
681 | */ | 753 | */ |
682 | if (cpuid != real_hard_smp_processor_id()) | 754 | if (cpuid != real_hard_smp_processor_id()) |
683 | continue; | 755 | continue; |
684 | cpuid = 0; | 756 | cpuid = 0; |
685 | #endif | 757 | #endif |
686 | 758 | ||
687 | c = &cpu_data(cpuid); | 759 | c = &cpu_data(cpuid); |
688 | c->clock_tick = *cfreq; | 760 | c->clock_tick = *cfreq; |
689 | 761 | ||
690 | tb = &trap_block[cpuid]; | 762 | tb = &trap_block[cpuid]; |
691 | get_mondo_data(hp, mp, tb); | 763 | get_mondo_data(hp, mp, tb); |
692 | 764 | ||
693 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | 765 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
694 | u64 j, t = mdesc_arc_target(hp, a); | 766 | u64 j, t = mdesc_arc_target(hp, a); |
695 | const char *t_name; | 767 | const char *t_name; |
696 | 768 | ||
697 | t_name = mdesc_node_name(hp, t); | 769 | t_name = mdesc_node_name(hp, t); |
698 | if (!strcmp(t_name, "cache")) { | 770 | if (!strcmp(t_name, "cache")) { |
699 | fill_in_one_cache(c, hp, t); | 771 | fill_in_one_cache(c, hp, t); |
700 | continue; | 772 | continue; |
701 | } | 773 | } |
702 | 774 | ||
703 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) { | 775 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) { |
704 | u64 n = mdesc_arc_target(hp, j); | 776 | u64 n = mdesc_arc_target(hp, j); |
705 | const char *n_name; | 777 | const char *n_name; |
706 | 778 | ||
707 | n_name = mdesc_node_name(hp, n); | 779 | n_name = mdesc_node_name(hp, n); |
708 | if (!strcmp(n_name, "cache")) | 780 | if (!strcmp(n_name, "cache")) |
709 | fill_in_one_cache(c, hp, n); | 781 | fill_in_one_cache(c, hp, n); |
710 | } | 782 | } |
711 | } | 783 | } |
712 | 784 | ||
713 | #ifdef CONFIG_SMP | 785 | #ifdef CONFIG_SMP |
714 | cpu_set(cpuid, cpu_present_map); | 786 | cpu_set(cpuid, cpu_present_map); |
715 | #endif | 787 | #endif |
716 | 788 | ||
717 | c->core_id = 0; | 789 | c->core_id = 0; |
718 | c->proc_id = -1; | 790 | c->proc_id = -1; |
719 | } | 791 | } |
720 | 792 | ||
721 | #ifdef CONFIG_SMP | 793 | #ifdef CONFIG_SMP |
722 | sparc64_multi_core = 1; | 794 | sparc64_multi_core = 1; |
723 | #endif | 795 | #endif |
724 | 796 | ||
725 | set_core_ids(hp); | 797 | set_core_ids(hp); |
726 | set_proc_ids(hp); | 798 | set_proc_ids(hp); |
727 | 799 | ||
728 | smp_fill_in_sib_core_maps(); | 800 | smp_fill_in_sib_core_maps(); |
729 | 801 | ||
730 | mdesc_release(hp); | 802 | mdesc_release(hp); |
731 | } | 803 | } |
732 | 804 | ||
733 | void __init sun4v_mdesc_init(void) | 805 | void __init sun4v_mdesc_init(void) |
734 | { | 806 | { |
735 | struct mdesc_handle *hp; | 807 | struct mdesc_handle *hp; |
736 | unsigned long len, real_len, status; | 808 | unsigned long len, real_len, status; |
737 | cpumask_t mask; | 809 | cpumask_t mask; |
738 | 810 | ||
739 | (void) sun4v_mach_desc(0UL, 0UL, &len); | 811 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
740 | 812 | ||
741 | printk("MDESC: Size is %lu bytes.\n", len); | 813 | printk("MDESC: Size is %lu bytes.\n", len); |
742 | 814 | ||
743 | hp = mdesc_alloc(len, &bootmem_mdesc_memops); | 815 | hp = mdesc_alloc(len, &bootmem_mdesc_memops); |
744 | if (hp == NULL) { | 816 | if (hp == NULL) { |
745 | prom_printf("MDESC: alloc of %lu bytes failed.\n", len); | 817 | prom_printf("MDESC: alloc of %lu bytes failed.\n", len); |
746 | prom_halt(); | 818 | prom_halt(); |
747 | } | 819 | } |
748 | 820 | ||
749 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | 821 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
750 | if (status != HV_EOK || real_len > len) { | 822 | if (status != HV_EOK || real_len > len) { |
751 | prom_printf("sun4v_mach_desc fails, err(%lu), " | 823 | prom_printf("sun4v_mach_desc fails, err(%lu), " |
752 | "len(%lu), real_len(%lu)\n", | 824 | "len(%lu), real_len(%lu)\n", |
753 | status, len, real_len); | 825 | status, len, real_len); |
754 | mdesc_free(hp); | 826 | mdesc_free(hp); |
755 | prom_halt(); | 827 | prom_halt(); |
756 | } | 828 | } |
757 | 829 | ||
758 | cur_mdesc = hp; | 830 | cur_mdesc = hp; |
759 | 831 | ||
760 | report_platform_properties(); | 832 | report_platform_properties(); |
761 | 833 | ||
762 | cpus_setall(mask); | 834 | cpus_setall(mask); |
763 | mdesc_fill_in_cpu_data(mask); | 835 | mdesc_fill_in_cpu_data(mask); |
764 | } | 836 | } |
765 | 837 |
arch/sparc64/kernel/vio.c
1 | /* vio.c: Virtual I/O channel devices probing infrastructure. | 1 | /* vio.c: Virtual I/O channel devices probing infrastructure. |
2 | * | 2 | * |
3 | * Copyright (c) 2003-2005 IBM Corp. | 3 | * Copyright (c) 2003-2005 IBM Corp. |
4 | * Dave Engebretsen engebret@us.ibm.com | 4 | * Dave Engebretsen engebret@us.ibm.com |
5 | * Santiago Leon santil@us.ibm.com | 5 | * Santiago Leon santil@us.ibm.com |
6 | * Hollis Blanchard <hollisb@us.ibm.com> | 6 | * Hollis Blanchard <hollisb@us.ibm.com> |
7 | * Stephen Rothwell | 7 | * Stephen Rothwell |
8 | * | 8 | * |
9 | * Adapted to sparc64 by David S. Miller davem@davemloft.net | 9 | * Adapted to sparc64 by David S. Miller davem@davemloft.net |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/irq.h> | 13 | #include <linux/irq.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | 15 | ||
16 | #include <asm/mdesc.h> | 16 | #include <asm/mdesc.h> |
17 | #include <asm/vio.h> | 17 | #include <asm/vio.h> |
18 | 18 | ||
19 | static inline int find_in_proplist(const char *list, const char *match, | 19 | static inline int find_in_proplist(const char *list, const char *match, |
20 | int len) | 20 | int len) |
21 | { | 21 | { |
22 | while (len > 0) { | 22 | while (len > 0) { |
23 | int l; | 23 | int l; |
24 | 24 | ||
25 | if (!strcmp(list, match)) | 25 | if (!strcmp(list, match)) |
26 | return 1; | 26 | return 1; |
27 | l = strlen(list) + 1; | 27 | l = strlen(list) + 1; |
28 | list += l; | 28 | list += l; |
29 | len -= l; | 29 | len -= l; |
30 | } | 30 | } |
31 | return 0; | 31 | return 0; |
32 | } | 32 | } |
33 | 33 | ||
34 | static const struct vio_device_id *vio_match_device( | 34 | static const struct vio_device_id *vio_match_device( |
35 | const struct vio_device_id *matches, | 35 | const struct vio_device_id *matches, |
36 | const struct vio_dev *dev) | 36 | const struct vio_dev *dev) |
37 | { | 37 | { |
38 | const char *type, *compat; | 38 | const char *type, *compat; |
39 | int len; | 39 | int len; |
40 | 40 | ||
41 | type = dev->type; | 41 | type = dev->type; |
42 | compat = dev->compat; | 42 | compat = dev->compat; |
43 | len = dev->compat_len; | 43 | len = dev->compat_len; |
44 | 44 | ||
45 | while (matches->type[0] || matches->compat[0]) { | 45 | while (matches->type[0] || matches->compat[0]) { |
46 | int match = 1; | 46 | int match = 1; |
47 | if (matches->type[0]) | 47 | if (matches->type[0]) |
48 | match &= !strcmp(matches->type, type); | 48 | match &= !strcmp(matches->type, type); |
49 | 49 | ||
50 | if (matches->compat[0]) { | 50 | if (matches->compat[0]) { |
51 | match &= len && | 51 | match &= len && |
52 | find_in_proplist(compat, matches->compat, len); | 52 | find_in_proplist(compat, matches->compat, len); |
53 | } | 53 | } |
54 | if (match) | 54 | if (match) |
55 | return matches; | 55 | return matches; |
56 | matches++; | 56 | matches++; |
57 | } | 57 | } |
58 | return NULL; | 58 | return NULL; |
59 | } | 59 | } |
60 | 60 | ||
61 | static int vio_bus_match(struct device *dev, struct device_driver *drv) | 61 | static int vio_bus_match(struct device *dev, struct device_driver *drv) |
62 | { | 62 | { |
63 | struct vio_dev *vio_dev = to_vio_dev(dev); | 63 | struct vio_dev *vio_dev = to_vio_dev(dev); |
64 | struct vio_driver *vio_drv = to_vio_driver(drv); | 64 | struct vio_driver *vio_drv = to_vio_driver(drv); |
65 | const struct vio_device_id *matches = vio_drv->id_table; | 65 | const struct vio_device_id *matches = vio_drv->id_table; |
66 | 66 | ||
67 | if (!matches) | 67 | if (!matches) |
68 | return 0; | 68 | return 0; |
69 | 69 | ||
70 | return vio_match_device(matches, vio_dev) != NULL; | 70 | return vio_match_device(matches, vio_dev) != NULL; |
71 | } | 71 | } |
72 | 72 | ||
73 | static int vio_device_probe(struct device *dev) | 73 | static int vio_device_probe(struct device *dev) |
74 | { | 74 | { |
75 | struct vio_dev *vdev = to_vio_dev(dev); | 75 | struct vio_dev *vdev = to_vio_dev(dev); |
76 | struct vio_driver *drv = to_vio_driver(dev->driver); | 76 | struct vio_driver *drv = to_vio_driver(dev->driver); |
77 | const struct vio_device_id *id; | 77 | const struct vio_device_id *id; |
78 | int error = -ENODEV; | 78 | int error = -ENODEV; |
79 | 79 | ||
80 | if (drv->probe) { | 80 | if (drv->probe) { |
81 | id = vio_match_device(drv->id_table, vdev); | 81 | id = vio_match_device(drv->id_table, vdev); |
82 | if (id) | 82 | if (id) |
83 | error = drv->probe(vdev, id); | 83 | error = drv->probe(vdev, id); |
84 | } | 84 | } |
85 | 85 | ||
86 | return error; | 86 | return error; |
87 | } | 87 | } |
88 | 88 | ||
89 | static int vio_device_remove(struct device *dev) | 89 | static int vio_device_remove(struct device *dev) |
90 | { | 90 | { |
91 | struct vio_dev *vdev = to_vio_dev(dev); | 91 | struct vio_dev *vdev = to_vio_dev(dev); |
92 | struct vio_driver *drv = to_vio_driver(dev->driver); | 92 | struct vio_driver *drv = to_vio_driver(dev->driver); |
93 | 93 | ||
94 | if (drv->remove) | 94 | if (drv->remove) |
95 | return drv->remove(vdev); | 95 | return drv->remove(vdev); |
96 | 96 | ||
97 | return 1; | 97 | return 1; |
98 | } | 98 | } |
99 | 99 | ||
100 | static ssize_t devspec_show(struct device *dev, | 100 | static ssize_t devspec_show(struct device *dev, |
101 | struct device_attribute *attr, char *buf) | 101 | struct device_attribute *attr, char *buf) |
102 | { | 102 | { |
103 | struct vio_dev *vdev = to_vio_dev(dev); | 103 | struct vio_dev *vdev = to_vio_dev(dev); |
104 | const char *str = "none"; | 104 | const char *str = "none"; |
105 | 105 | ||
106 | if (!strcmp(vdev->type, "network")) | 106 | if (!strcmp(vdev->type, "network")) |
107 | str = "vnet"; | 107 | str = "vnet"; |
108 | else if (!strcmp(vdev->type, "block")) | 108 | else if (!strcmp(vdev->type, "block")) |
109 | str = "vdisk"; | 109 | str = "vdisk"; |
110 | 110 | ||
111 | return sprintf(buf, "%s\n", str); | 111 | return sprintf(buf, "%s\n", str); |
112 | } | 112 | } |
113 | 113 | ||
114 | static ssize_t type_show(struct device *dev, | 114 | static ssize_t type_show(struct device *dev, |
115 | struct device_attribute *attr, char *buf) | 115 | struct device_attribute *attr, char *buf) |
116 | { | 116 | { |
117 | struct vio_dev *vdev = to_vio_dev(dev); | 117 | struct vio_dev *vdev = to_vio_dev(dev); |
118 | return sprintf(buf, "%s\n", vdev->type); | 118 | return sprintf(buf, "%s\n", vdev->type); |
119 | } | 119 | } |
120 | 120 | ||
121 | static struct device_attribute vio_dev_attrs[] = { | 121 | static struct device_attribute vio_dev_attrs[] = { |
122 | __ATTR_RO(devspec), | 122 | __ATTR_RO(devspec), |
123 | __ATTR_RO(type), | 123 | __ATTR_RO(type), |
124 | __ATTR_NULL | 124 | __ATTR_NULL |
125 | }; | 125 | }; |
126 | 126 | ||
127 | static struct bus_type vio_bus_type = { | 127 | static struct bus_type vio_bus_type = { |
128 | .name = "vio", | 128 | .name = "vio", |
129 | .dev_attrs = vio_dev_attrs, | 129 | .dev_attrs = vio_dev_attrs, |
130 | .match = vio_bus_match, | 130 | .match = vio_bus_match, |
131 | .probe = vio_device_probe, | 131 | .probe = vio_device_probe, |
132 | .remove = vio_device_remove, | 132 | .remove = vio_device_remove, |
133 | }; | 133 | }; |
134 | 134 | ||
135 | int vio_register_driver(struct vio_driver *viodrv) | 135 | int vio_register_driver(struct vio_driver *viodrv) |
136 | { | 136 | { |
137 | viodrv->driver.bus = &vio_bus_type; | 137 | viodrv->driver.bus = &vio_bus_type; |
138 | 138 | ||
139 | return driver_register(&viodrv->driver); | 139 | return driver_register(&viodrv->driver); |
140 | } | 140 | } |
141 | EXPORT_SYMBOL(vio_register_driver); | 141 | EXPORT_SYMBOL(vio_register_driver); |
142 | 142 | ||
143 | void vio_unregister_driver(struct vio_driver *viodrv) | 143 | void vio_unregister_driver(struct vio_driver *viodrv) |
144 | { | 144 | { |
145 | driver_unregister(&viodrv->driver); | 145 | driver_unregister(&viodrv->driver); |
146 | } | 146 | } |
147 | EXPORT_SYMBOL(vio_unregister_driver); | 147 | EXPORT_SYMBOL(vio_unregister_driver); |
148 | 148 | ||
149 | static void __devinit vio_dev_release(struct device *dev) | 149 | static void __devinit vio_dev_release(struct device *dev) |
150 | { | 150 | { |
151 | kfree(to_vio_dev(dev)); | 151 | kfree(to_vio_dev(dev)); |
152 | } | 152 | } |
153 | 153 | ||
154 | static ssize_t | 154 | static ssize_t |
155 | show_pciobppath_attr(struct device *dev, struct device_attribute *attr, | 155 | show_pciobppath_attr(struct device *dev, struct device_attribute *attr, |
156 | char *buf) | 156 | char *buf) |
157 | { | 157 | { |
158 | struct vio_dev *vdev; | 158 | struct vio_dev *vdev; |
159 | struct device_node *dp; | 159 | struct device_node *dp; |
160 | 160 | ||
161 | vdev = to_vio_dev(dev); | 161 | vdev = to_vio_dev(dev); |
162 | dp = vdev->dp; | 162 | dp = vdev->dp; |
163 | 163 | ||
164 | return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name); | 164 | return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name); |
165 | } | 165 | } |
166 | 166 | ||
167 | static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, | 167 | static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, |
168 | show_pciobppath_attr, NULL); | 168 | show_pciobppath_attr, NULL); |
169 | 169 | ||
170 | struct device_node *cdev_node; | 170 | struct device_node *cdev_node; |
171 | 171 | ||
172 | static struct vio_dev *root_vdev; | 172 | static struct vio_dev *root_vdev; |
173 | static u64 cdev_cfg_handle; | 173 | static u64 cdev_cfg_handle; |
174 | 174 | ||
175 | static void vio_add(struct mdesc_handle *hp, u64 node) | ||
176 | { | ||
177 | const char *name = mdesc_get_property(hp, node, "name", NULL); | ||
178 | const u64 *id = mdesc_get_property(hp, node, "id", NULL); | ||
179 | |||
180 | printk(KERN_ERR "VIO: Device add (%s) ID[%lx]\n", | ||
181 | name, *id); | ||
182 | } | ||
183 | |||
184 | static void vio_remove(struct mdesc_handle *hp, u64 node) | ||
185 | { | ||
186 | const char *name = mdesc_get_property(hp, node, "name", NULL); | ||
187 | const u64 *id = mdesc_get_property(hp, node, "id", NULL); | ||
188 | |||
189 | printk(KERN_ERR "VIO: Device remove (%s) ID[%lx]\n", | ||
190 | name, *id); | ||
191 | } | ||
192 | |||
193 | static struct mdesc_notifier_client vio_device_notifier = { | ||
194 | .add = vio_add, | ||
195 | .remove = vio_remove, | ||
196 | .node_name = "virtual-device-port", | ||
197 | }; | ||
198 | |||
199 | static struct mdesc_notifier_client vio_ds_notifier = { | ||
200 | .add = vio_add, | ||
201 | .remove = vio_remove, | ||
202 | .node_name = "domain-services-port", | ||
203 | }; | ||
204 | |||
175 | static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, | 205 | static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, |
176 | struct vio_dev *vdev) | 206 | struct vio_dev *vdev) |
177 | { | 207 | { |
178 | u64 a; | 208 | u64 a; |
179 | 209 | ||
180 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | 210 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
181 | const u64 *chan_id; | 211 | const u64 *chan_id; |
182 | const u64 *irq; | 212 | const u64 *irq; |
183 | u64 target; | 213 | u64 target; |
184 | 214 | ||
185 | target = mdesc_arc_target(hp, a); | 215 | target = mdesc_arc_target(hp, a); |
186 | 216 | ||
187 | irq = mdesc_get_property(hp, target, "tx-ino", NULL); | 217 | irq = mdesc_get_property(hp, target, "tx-ino", NULL); |
188 | if (irq) | 218 | if (irq) |
189 | vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); | 219 | vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); |
190 | 220 | ||
191 | irq = mdesc_get_property(hp, target, "rx-ino", NULL); | 221 | irq = mdesc_get_property(hp, target, "rx-ino", NULL); |
192 | if (irq) | 222 | if (irq) |
193 | vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); | 223 | vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); |
194 | 224 | ||
195 | chan_id = mdesc_get_property(hp, target, "id", NULL); | 225 | chan_id = mdesc_get_property(hp, target, "id", NULL); |
196 | if (chan_id) | 226 | if (chan_id) |
197 | vdev->channel_id = *chan_id; | 227 | vdev->channel_id = *chan_id; |
198 | } | 228 | } |
199 | } | 229 | } |
200 | 230 | ||
201 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | 231 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, |
202 | struct device *parent) | 232 | struct device *parent) |
203 | { | 233 | { |
204 | const char *type, *compat; | 234 | const char *type, *compat; |
205 | struct device_node *dp; | 235 | struct device_node *dp; |
206 | struct vio_dev *vdev; | 236 | struct vio_dev *vdev; |
207 | int err, tlen, clen; | 237 | int err, tlen, clen; |
208 | 238 | ||
209 | type = mdesc_get_property(hp, mp, "device-type", &tlen); | 239 | type = mdesc_get_property(hp, mp, "device-type", &tlen); |
210 | if (!type) { | 240 | if (!type) { |
211 | type = mdesc_get_property(hp, mp, "name", &tlen); | 241 | type = mdesc_get_property(hp, mp, "name", &tlen); |
212 | if (!type) { | 242 | if (!type) { |
213 | type = mdesc_node_name(hp, mp); | 243 | type = mdesc_node_name(hp, mp); |
214 | tlen = strlen(type) + 1; | 244 | tlen = strlen(type) + 1; |
215 | } | 245 | } |
216 | } | 246 | } |
217 | if (tlen > VIO_MAX_TYPE_LEN) { | 247 | if (tlen > VIO_MAX_TYPE_LEN) { |
218 | printk(KERN_ERR "VIO: Type string [%s] is too long.\n", | 248 | printk(KERN_ERR "VIO: Type string [%s] is too long.\n", |
219 | type); | 249 | type); |
220 | return NULL; | 250 | return NULL; |
221 | } | 251 | } |
222 | 252 | ||
223 | compat = mdesc_get_property(hp, mp, "device-type", &clen); | 253 | compat = mdesc_get_property(hp, mp, "device-type", &clen); |
224 | if (!compat) { | 254 | if (!compat) { |
225 | clen = 0; | 255 | clen = 0; |
226 | } else if (clen > VIO_MAX_COMPAT_LEN) { | 256 | } else if (clen > VIO_MAX_COMPAT_LEN) { |
227 | printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n", | 257 | printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n", |
228 | clen, type); | 258 | clen, type); |
229 | return NULL; | 259 | return NULL; |
230 | } | 260 | } |
231 | 261 | ||
232 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); | 262 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); |
233 | if (!vdev) { | 263 | if (!vdev) { |
234 | printk(KERN_ERR "VIO: Could not allocate vio_dev\n"); | 264 | printk(KERN_ERR "VIO: Could not allocate vio_dev\n"); |
235 | return NULL; | 265 | return NULL; |
236 | } | 266 | } |
237 | 267 | ||
238 | vdev->mp = mp; | 268 | vdev->mp = mp; |
239 | memcpy(vdev->type, type, tlen); | 269 | memcpy(vdev->type, type, tlen); |
240 | if (compat) | 270 | if (compat) |
241 | memcpy(vdev->compat, compat, clen); | 271 | memcpy(vdev->compat, compat, clen); |
242 | else | 272 | else |
243 | memset(vdev->compat, 0, sizeof(vdev->compat)); | 273 | memset(vdev->compat, 0, sizeof(vdev->compat)); |
244 | vdev->compat_len = clen; | 274 | vdev->compat_len = clen; |
245 | 275 | ||
246 | vdev->channel_id = ~0UL; | 276 | vdev->channel_id = ~0UL; |
247 | vdev->tx_irq = ~0; | 277 | vdev->tx_irq = ~0; |
248 | vdev->rx_irq = ~0; | 278 | vdev->rx_irq = ~0; |
249 | 279 | ||
250 | vio_fill_channel_info(hp, mp, vdev); | 280 | vio_fill_channel_info(hp, mp, vdev); |
251 | 281 | ||
252 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); | 282 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); |
253 | vdev->dev.parent = parent; | 283 | vdev->dev.parent = parent; |
254 | vdev->dev.bus = &vio_bus_type; | 284 | vdev->dev.bus = &vio_bus_type; |
255 | vdev->dev.release = vio_dev_release; | 285 | vdev->dev.release = vio_dev_release; |
256 | 286 | ||
257 | if (parent == NULL) { | 287 | if (parent == NULL) { |
258 | dp = cdev_node; | 288 | dp = cdev_node; |
259 | } else if (to_vio_dev(parent) == root_vdev) { | 289 | } else if (to_vio_dev(parent) == root_vdev) { |
260 | dp = of_get_next_child(cdev_node, NULL); | 290 | dp = of_get_next_child(cdev_node, NULL); |
261 | while (dp) { | 291 | while (dp) { |
262 | if (!strcmp(dp->type, type)) | 292 | if (!strcmp(dp->type, type)) |
263 | break; | 293 | break; |
264 | 294 | ||
265 | dp = of_get_next_child(cdev_node, dp); | 295 | dp = of_get_next_child(cdev_node, dp); |
266 | } | 296 | } |
267 | } else { | 297 | } else { |
268 | dp = to_vio_dev(parent)->dp; | 298 | dp = to_vio_dev(parent)->dp; |
269 | } | 299 | } |
270 | vdev->dp = dp; | 300 | vdev->dp = dp; |
271 | 301 | ||
272 | err = device_register(&vdev->dev); | 302 | err = device_register(&vdev->dev); |
273 | if (err) { | 303 | if (err) { |
274 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", | 304 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", |
275 | vdev->dev.bus_id, err); | 305 | vdev->dev.bus_id, err); |
276 | kfree(vdev); | 306 | kfree(vdev); |
277 | return NULL; | 307 | return NULL; |
278 | } | 308 | } |
279 | if (vdev->dp) | 309 | if (vdev->dp) |
280 | err = sysfs_create_file(&vdev->dev.kobj, | 310 | err = sysfs_create_file(&vdev->dev.kobj, |
281 | &dev_attr_obppath.attr); | 311 | &dev_attr_obppath.attr); |
282 | 312 | ||
283 | return vdev; | 313 | return vdev; |
284 | } | 314 | } |
285 | 315 | ||
286 | static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) | 316 | static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) |
287 | { | 317 | { |
288 | u64 a; | 318 | u64 a; |
289 | 319 | ||
290 | mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { | 320 | mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { |
291 | struct vio_dev *vdev; | 321 | struct vio_dev *vdev; |
292 | u64 target; | 322 | u64 target; |
293 | 323 | ||
294 | target = mdesc_arc_target(hp, a); | 324 | target = mdesc_arc_target(hp, a); |
295 | vdev = vio_create_one(hp, target, &parent->dev); | 325 | vdev = vio_create_one(hp, target, &parent->dev); |
296 | if (vdev) | 326 | if (vdev) |
297 | walk_tree(hp, target, vdev); | 327 | walk_tree(hp, target, vdev); |
298 | } | 328 | } |
299 | } | 329 | } |
300 | 330 | ||
301 | static void create_devices(struct mdesc_handle *hp, u64 root) | 331 | static void create_devices(struct mdesc_handle *hp, u64 root) |
302 | { | 332 | { |
303 | u64 mp; | 333 | u64 mp; |
304 | 334 | ||
305 | root_vdev = vio_create_one(hp, root, NULL); | 335 | root_vdev = vio_create_one(hp, root, NULL); |
306 | if (!root_vdev) { | 336 | if (!root_vdev) { |
307 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | 337 | printk(KERN_ERR "VIO: Coult not create root device.\n"); |
308 | return; | 338 | return; |
309 | } | 339 | } |
310 | 340 | ||
311 | walk_tree(hp, root, root_vdev); | 341 | walk_tree(hp, root, root_vdev); |
312 | 342 | ||
313 | /* Domain services is odd as it doesn't sit underneath the | 343 | /* Domain services is odd as it doesn't sit underneath the |
314 | * channel-devices node, so we plug it in manually. | 344 | * channel-devices node, so we plug it in manually. |
315 | */ | 345 | */ |
316 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); | 346 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); |
317 | if (mp != MDESC_NODE_NULL) { | 347 | if (mp != MDESC_NODE_NULL) { |
318 | struct vio_dev *parent = vio_create_one(hp, mp, | 348 | struct vio_dev *parent = vio_create_one(hp, mp, |
319 | &root_vdev->dev); | 349 | &root_vdev->dev); |
320 | 350 | ||
321 | if (parent) | 351 | if (parent) |
322 | walk_tree(hp, mp, parent); | 352 | walk_tree(hp, mp, parent); |
323 | } | 353 | } |
324 | } | 354 | } |
325 | 355 | ||
326 | const char *channel_devices_node = "channel-devices"; | 356 | const char *channel_devices_node = "channel-devices"; |
327 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; | 357 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; |
328 | const char *cfg_handle_prop = "cfg-handle"; | 358 | const char *cfg_handle_prop = "cfg-handle"; |
329 | 359 | ||
330 | static int __init vio_init(void) | 360 | static int __init vio_init(void) |
331 | { | 361 | { |
332 | struct mdesc_handle *hp; | 362 | struct mdesc_handle *hp; |
333 | const char *compat; | 363 | const char *compat; |
334 | const u64 *cfg_handle; | 364 | const u64 *cfg_handle; |
335 | int err, len; | 365 | int err, len; |
336 | u64 root; | 366 | u64 root; |
337 | 367 | ||
338 | err = bus_register(&vio_bus_type); | 368 | err = bus_register(&vio_bus_type); |
339 | if (err) { | 369 | if (err) { |
340 | printk(KERN_ERR "VIO: Could not register bus type err=%d\n", | 370 | printk(KERN_ERR "VIO: Could not register bus type err=%d\n", |
341 | err); | 371 | err); |
342 | return err; | 372 | return err; |
343 | } | 373 | } |
344 | 374 | ||
345 | hp = mdesc_grab(); | 375 | hp = mdesc_grab(); |
346 | if (!hp) | 376 | if (!hp) |
347 | return 0; | 377 | return 0; |
348 | 378 | ||
349 | root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node); | 379 | root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node); |
350 | if (root == MDESC_NODE_NULL) { | 380 | if (root == MDESC_NODE_NULL) { |
351 | printk(KERN_INFO "VIO: No channel-devices MDESC node.\n"); | 381 | printk(KERN_INFO "VIO: No channel-devices MDESC node.\n"); |
352 | mdesc_release(hp); | 382 | mdesc_release(hp); |
353 | return 0; | 383 | return 0; |
354 | } | 384 | } |
355 | 385 | ||
356 | cdev_node = of_find_node_by_name(NULL, "channel-devices"); | 386 | cdev_node = of_find_node_by_name(NULL, "channel-devices"); |
357 | err = -ENODEV; | 387 | err = -ENODEV; |
358 | if (!cdev_node) { | 388 | if (!cdev_node) { |
359 | printk(KERN_INFO "VIO: No channel-devices OBP node.\n"); | 389 | printk(KERN_INFO "VIO: No channel-devices OBP node.\n"); |
360 | goto out_release; | 390 | goto out_release; |
361 | } | 391 | } |
362 | 392 | ||
363 | compat = mdesc_get_property(hp, root, "compatible", &len); | 393 | compat = mdesc_get_property(hp, root, "compatible", &len); |
364 | if (!compat) { | 394 | if (!compat) { |
365 | printk(KERN_ERR "VIO: Channel devices lacks compatible " | 395 | printk(KERN_ERR "VIO: Channel devices lacks compatible " |
366 | "property\n"); | 396 | "property\n"); |
367 | goto out_release; | 397 | goto out_release; |
368 | } | 398 | } |
369 | if (!find_in_proplist(compat, channel_devices_compat, len)) { | 399 | if (!find_in_proplist(compat, channel_devices_compat, len)) { |
370 | printk(KERN_ERR "VIO: Channel devices node lacks (%s) " | 400 | printk(KERN_ERR "VIO: Channel devices node lacks (%s) " |
371 | "compat entry.\n", channel_devices_compat); | 401 | "compat entry.\n", channel_devices_compat); |
372 | goto out_release; | 402 | goto out_release; |
373 | } | 403 | } |
374 | 404 | ||
375 | cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL); | 405 | cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL); |
376 | if (!cfg_handle) { | 406 | if (!cfg_handle) { |
377 | printk(KERN_ERR "VIO: Channel devices lacks %s property\n", | 407 | printk(KERN_ERR "VIO: Channel devices lacks %s property\n", |
378 | cfg_handle_prop); | 408 | cfg_handle_prop); |
379 | goto out_release; | 409 | goto out_release; |
380 | } | 410 | } |
381 | 411 | ||
382 | cdev_cfg_handle = *cfg_handle; | 412 | cdev_cfg_handle = *cfg_handle; |
413 | |||
414 | mdesc_register_notifier(&vio_device_notifier); | ||
415 | mdesc_register_notifier(&vio_ds_notifier); | ||
383 | 416 | ||
384 | create_devices(hp, root); | 417 | create_devices(hp, root); |
385 | 418 | ||
386 | mdesc_release(hp); | 419 | mdesc_release(hp); |
387 | 420 | ||
388 | return 0; | 421 | return 0; |
389 | 422 | ||
390 | out_release: | 423 | out_release: |
391 | mdesc_release(hp); | 424 | mdesc_release(hp); |
392 | return err; | 425 | return err; |
393 | } | 426 | } |
394 | 427 | ||
395 | postcore_initcall(vio_init); | 428 | postcore_initcall(vio_init); |
396 | 429 |
include/asm-sparc64/mdesc.h
1 | #ifndef _SPARC64_MDESC_H | 1 | #ifndef _SPARC64_MDESC_H |
2 | #define _SPARC64_MDESC_H | 2 | #define _SPARC64_MDESC_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <linux/cpumask.h> | 5 | #include <linux/cpumask.h> |
6 | #include <asm/prom.h> | 6 | #include <asm/prom.h> |
7 | 7 | ||
8 | struct mdesc_handle; | 8 | struct mdesc_handle; |
9 | 9 | ||
10 | /* Machine description operations are to be surrounded by grab and | 10 | /* Machine description operations are to be surrounded by grab and |
11 | * release calls. The mdesc_handle returned from the grab is | 11 | * release calls. The mdesc_handle returned from the grab is |
12 | * the first argument to all of the operational calls that work | 12 | * the first argument to all of the operational calls that work |
13 | * on mdescs. | 13 | * on mdescs. |
14 | */ | 14 | */ |
15 | extern struct mdesc_handle *mdesc_grab(void); | 15 | extern struct mdesc_handle *mdesc_grab(void); |
16 | extern void mdesc_release(struct mdesc_handle *); | 16 | extern void mdesc_release(struct mdesc_handle *); |
17 | 17 | ||
18 | #define MDESC_NODE_NULL (~(u64)0) | 18 | #define MDESC_NODE_NULL (~(u64)0) |
19 | 19 | ||
20 | extern u64 mdesc_node_by_name(struct mdesc_handle *handle, | 20 | extern u64 mdesc_node_by_name(struct mdesc_handle *handle, |
21 | u64 from_node, const char *name); | 21 | u64 from_node, const char *name); |
22 | #define mdesc_for_each_node_by_name(__hdl, __node, __name) \ | 22 | #define mdesc_for_each_node_by_name(__hdl, __node, __name) \ |
23 | for (__node = mdesc_node_by_name(__hdl, MDESC_NODE_NULL, __name); \ | 23 | for (__node = mdesc_node_by_name(__hdl, MDESC_NODE_NULL, __name); \ |
24 | (__node) != MDESC_NODE_NULL; \ | 24 | (__node) != MDESC_NODE_NULL; \ |
25 | __node = mdesc_node_by_name(__hdl, __node, __name)) | 25 | __node = mdesc_node_by_name(__hdl, __node, __name)) |
26 | 26 | ||
27 | /* Access to property values returned from mdesc_get_property() are | 27 | /* Access to property values returned from mdesc_get_property() are |
28 | * only valid inside of a mdesc_grab()/mdesc_release() sequence. | 28 | * only valid inside of a mdesc_grab()/mdesc_release() sequence. |
29 | * Once mdesc_release() is called, the memory backed up by these | 29 | * Once mdesc_release() is called, the memory backed up by these |
30 | * pointers may reference freed up memory. | 30 | * pointers may reference freed up memory. |
31 | * | 31 | * |
32 | * Therefore callers must make copies of any property values | 32 | * Therefore callers must make copies of any property values |
33 | * they need. | 33 | * they need. |
34 | * | 34 | * |
35 | * These same rules apply to mdesc_node_name(). | 35 | * These same rules apply to mdesc_node_name(). |
36 | */ | 36 | */ |
37 | extern const void *mdesc_get_property(struct mdesc_handle *handle, | 37 | extern const void *mdesc_get_property(struct mdesc_handle *handle, |
38 | u64 node, const char *name, int *lenp); | 38 | u64 node, const char *name, int *lenp); |
39 | extern const char *mdesc_node_name(struct mdesc_handle *hp, u64 node); | 39 | extern const char *mdesc_node_name(struct mdesc_handle *hp, u64 node); |
40 | 40 | ||
41 | /* MD arc iteration, the standard sequence is: | 41 | /* MD arc iteration, the standard sequence is: |
42 | * | 42 | * |
43 | * unsigned long arc; | 43 | * unsigned long arc; |
44 | * mdesc_for_each_arc(arc, handle, node, MDESC_ARC_TYPE_{FWD,BACK}) { | 44 | * mdesc_for_each_arc(arc, handle, node, MDESC_ARC_TYPE_{FWD,BACK}) { |
45 | * unsigned long target = mdesc_arc_target(handle, arc); | 45 | * unsigned long target = mdesc_arc_target(handle, arc); |
46 | * ... | 46 | * ... |
47 | * } | 47 | * } |
48 | */ | 48 | */ |
49 | 49 | ||
50 | #define MDESC_ARC_TYPE_FWD "fwd" | 50 | #define MDESC_ARC_TYPE_FWD "fwd" |
51 | #define MDESC_ARC_TYPE_BACK "back" | 51 | #define MDESC_ARC_TYPE_BACK "back" |
52 | 52 | ||
53 | extern u64 mdesc_next_arc(struct mdesc_handle *handle, u64 from, | 53 | extern u64 mdesc_next_arc(struct mdesc_handle *handle, u64 from, |
54 | const char *arc_type); | 54 | const char *arc_type); |
55 | #define mdesc_for_each_arc(__arc, __hdl, __node, __type) \ | 55 | #define mdesc_for_each_arc(__arc, __hdl, __node, __type) \ |
56 | for (__arc = mdesc_next_arc(__hdl, __node, __type); \ | 56 | for (__arc = mdesc_next_arc(__hdl, __node, __type); \ |
57 | (__arc) != MDESC_NODE_NULL; \ | 57 | (__arc) != MDESC_NODE_NULL; \ |
58 | __arc = mdesc_next_arc(__hdl, __arc, __type)) | 58 | __arc = mdesc_next_arc(__hdl, __arc, __type)) |
59 | 59 | ||
60 | extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc); | 60 | extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc); |
61 | 61 | ||
62 | extern void mdesc_update(void); | 62 | extern void mdesc_update(void); |
63 | 63 | ||
64 | struct mdesc_notifier_client { | ||
65 | void (*add)(struct mdesc_handle *handle, u64 node); | ||
66 | void (*remove)(struct mdesc_handle *handle, u64 node); | ||
67 | |||
68 | const char *node_name; | ||
69 | struct mdesc_notifier_client *next; | ||
70 | }; | ||
71 | |||
72 | extern void mdesc_register_notifier(struct mdesc_notifier_client *client); | ||
73 | |||
64 | extern void mdesc_fill_in_cpu_data(cpumask_t mask); | 74 | extern void mdesc_fill_in_cpu_data(cpumask_t mask); |
65 | 75 | ||
66 | extern void sun4v_mdesc_init(void); | 76 | extern void sun4v_mdesc_init(void); |
67 | 77 | ||
68 | #endif | 78 | #endif |
69 | 79 |