diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries/vio.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 556 |
1 files changed, 0 insertions, 556 deletions
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c deleted file mode 100644 index 04be62d368a6..000000000000 --- a/arch/powerpc/platforms/iseries/vio.c +++ /dev/null | |||
@@ -1,556 +0,0 @@ | |||
1 | /* | ||
2 | * Legacy iSeries specific vio initialisation | ||
3 | * that needs to be built in (not a module). | ||
4 | * | ||
5 | * © Copyright 2007 IBM Corporation | ||
6 | * Author: Stephen Rothwell | ||
7 | * Some parts collected from various other files | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation; either version 2 of the | ||
12 | * License, or (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software Foundation, | ||
21 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/completion.h> | ||
27 | #include <linux/proc_fs.h> | ||
28 | #include <linux/export.h> | ||
29 | |||
30 | #include <asm/firmware.h> | ||
31 | #include <asm/vio.h> | ||
32 | #include <asm/iseries/vio.h> | ||
33 | #include <asm/iseries/iommu.h> | ||
34 | #include <asm/iseries/hv_types.h> | ||
35 | #include <asm/iseries/hv_lp_event.h> | ||
36 | |||
37 | #define FIRST_VTY 0 | ||
38 | #define NUM_VTYS 1 | ||
39 | #define FIRST_VSCSI (FIRST_VTY + NUM_VTYS) | ||
40 | #define NUM_VSCSIS 1 | ||
41 | #define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS) | ||
42 | #define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS | ||
43 | #define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS) | ||
44 | #define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS | ||
45 | #define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS) | ||
46 | #define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS | ||
47 | #define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS) | ||
48 | #define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES | ||
49 | |||
50 | struct vio_waitevent { | ||
51 | struct completion com; | ||
52 | int rc; | ||
53 | u16 sub_result; | ||
54 | }; | ||
55 | |||
56 | struct vio_resource { | ||
57 | char rsrcname[10]; | ||
58 | char type[4]; | ||
59 | char model[3]; | ||
60 | }; | ||
61 | |||
62 | static struct property *new_property(const char *name, int length, | ||
63 | const void *value) | ||
64 | { | ||
65 | struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, | ||
66 | GFP_KERNEL); | ||
67 | |||
68 | if (!np) | ||
69 | return NULL; | ||
70 | np->name = (char *)(np + 1); | ||
71 | np->value = np->name + strlen(name) + 1; | ||
72 | strcpy(np->name, name); | ||
73 | memcpy(np->value, value, length); | ||
74 | np->length = length; | ||
75 | return np; | ||
76 | } | ||
77 | |||
78 | static void free_property(struct property *np) | ||
79 | { | ||
80 | kfree(np); | ||
81 | } | ||
82 | |||
83 | static struct device_node *new_node(const char *path, | ||
84 | struct device_node *parent) | ||
85 | { | ||
86 | struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); | ||
87 | |||
88 | if (!np) | ||
89 | return NULL; | ||
90 | np->full_name = kstrdup(path, GFP_KERNEL); | ||
91 | if (!np->full_name) { | ||
92 | kfree(np); | ||
93 | return NULL; | ||
94 | } | ||
95 | of_node_set_flag(np, OF_DYNAMIC); | ||
96 | kref_init(&np->kref); | ||
97 | np->parent = of_node_get(parent); | ||
98 | return np; | ||
99 | } | ||
100 | |||
101 | static void free_node(struct device_node *np) | ||
102 | { | ||
103 | struct property *next; | ||
104 | struct property *prop; | ||
105 | |||
106 | next = np->properties; | ||
107 | while (next) { | ||
108 | prop = next; | ||
109 | next = prop->next; | ||
110 | free_property(prop); | ||
111 | } | ||
112 | of_node_put(np->parent); | ||
113 | kfree(np->full_name); | ||
114 | kfree(np); | ||
115 | } | ||
116 | |||
117 | static int add_string_property(struct device_node *np, const char *name, | ||
118 | const char *value) | ||
119 | { | ||
120 | struct property *nprop = new_property(name, strlen(value) + 1, value); | ||
121 | |||
122 | if (!nprop) | ||
123 | return 0; | ||
124 | prom_add_property(np, nprop); | ||
125 | return 1; | ||
126 | } | ||
127 | |||
128 | static int add_raw_property(struct device_node *np, const char *name, | ||
129 | int length, const void *value) | ||
130 | { | ||
131 | struct property *nprop = new_property(name, length, value); | ||
132 | |||
133 | if (!nprop) | ||
134 | return 0; | ||
135 | prom_add_property(np, nprop); | ||
136 | return 1; | ||
137 | } | ||
138 | |||
139 | static struct device_node *do_device_node(struct device_node *parent, | ||
140 | const char *name, u32 reg, u32 unit, const char *type, | ||
141 | const char *compat, struct vio_resource *res) | ||
142 | { | ||
143 | struct device_node *np; | ||
144 | char path[32]; | ||
145 | |||
146 | snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg); | ||
147 | np = new_node(path, parent); | ||
148 | if (!np) | ||
149 | return NULL; | ||
150 | if (!add_string_property(np, "name", name) || | ||
151 | !add_string_property(np, "device_type", type) || | ||
152 | !add_string_property(np, "compatible", compat) || | ||
153 | !add_raw_property(np, "reg", sizeof(reg), ®) || | ||
154 | !add_raw_property(np, "linux,unit_address", | ||
155 | sizeof(unit), &unit)) { | ||
156 | goto node_free; | ||
157 | } | ||
158 | if (res) { | ||
159 | if (!add_raw_property(np, "linux,vio_rsrcname", | ||
160 | sizeof(res->rsrcname), res->rsrcname) || | ||
161 | !add_raw_property(np, "linux,vio_type", | ||
162 | sizeof(res->type), res->type) || | ||
163 | !add_raw_property(np, "linux,vio_model", | ||
164 | sizeof(res->model), res->model)) | ||
165 | goto node_free; | ||
166 | } | ||
167 | np->name = of_get_property(np, "name", NULL); | ||
168 | np->type = of_get_property(np, "device_type", NULL); | ||
169 | of_attach_node(np); | ||
170 | #ifdef CONFIG_PROC_DEVICETREE | ||
171 | if (parent->pde) { | ||
172 | struct proc_dir_entry *ent; | ||
173 | |||
174 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde); | ||
175 | if (ent) | ||
176 | proc_device_tree_add_node(np, ent); | ||
177 | } | ||
178 | #endif | ||
179 | return np; | ||
180 | |||
181 | node_free: | ||
182 | free_node(np); | ||
183 | return NULL; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * This is here so that we can dynamically add viodasd | ||
188 | * devices without exposing all the above infrastructure. | ||
189 | */ | ||
190 | struct vio_dev *vio_create_viodasd(u32 unit) | ||
191 | { | ||
192 | struct device_node *vio_root; | ||
193 | struct device_node *np; | ||
194 | struct vio_dev *vdev = NULL; | ||
195 | |||
196 | vio_root = of_find_node_by_path("/vdevice"); | ||
197 | if (!vio_root) | ||
198 | return NULL; | ||
199 | np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | ||
200 | "block", "IBM,iSeries-viodasd", NULL); | ||
201 | of_node_put(vio_root); | ||
202 | if (np) { | ||
203 | vdev = vio_register_device_node(np); | ||
204 | if (!vdev) | ||
205 | free_node(np); | ||
206 | } | ||
207 | return vdev; | ||
208 | } | ||
209 | EXPORT_SYMBOL_GPL(vio_create_viodasd); | ||
210 | |||
211 | static void __init handle_block_event(struct HvLpEvent *event) | ||
212 | { | ||
213 | struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; | ||
214 | struct vio_waitevent *pwe; | ||
215 | |||
216 | if (event == NULL) | ||
217 | /* Notification that a partition went away! */ | ||
218 | return; | ||
219 | /* First, we should NEVER get an int here...only acks */ | ||
220 | if (hvlpevent_is_int(event)) { | ||
221 | printk(KERN_WARNING "handle_viod_request: " | ||
222 | "Yikes! got an int in viodasd event handler!\n"); | ||
223 | if (hvlpevent_need_ack(event)) { | ||
224 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
225 | HvCallEvent_ackLpEvent(event); | ||
226 | } | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | ||
231 | case vioblockopen: | ||
232 | /* | ||
233 | * Handle a response to an open request. We get all the | ||
234 | * disk information in the response, so update it. The | ||
235 | * correlation token contains a pointer to a waitevent | ||
236 | * structure that has a completion in it. update the | ||
237 | * return code in the waitevent structure and post the | ||
238 | * completion to wake up the guy who sent the request | ||
239 | */ | ||
240 | pwe = (struct vio_waitevent *)event->xCorrelationToken; | ||
241 | pwe->rc = event->xRc; | ||
242 | pwe->sub_result = bevent->sub_result; | ||
243 | complete(&pwe->com); | ||
244 | break; | ||
245 | case vioblockclose: | ||
246 | break; | ||
247 | default: | ||
248 | printk(KERN_WARNING "handle_viod_request: unexpected subtype!"); | ||
249 | if (hvlpevent_need_ack(event)) { | ||
250 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
251 | HvCallEvent_ackLpEvent(event); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | static void __init probe_disk(struct device_node *vio_root, u32 unit) | ||
257 | { | ||
258 | HvLpEvent_Rc hvrc; | ||
259 | struct vio_waitevent we; | ||
260 | u16 flags = 0; | ||
261 | |||
262 | retry: | ||
263 | init_completion(&we.com); | ||
264 | |||
265 | /* Send the open event to OS/400 */ | ||
266 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
267 | HvLpEvent_Type_VirtualIo, | ||
268 | viomajorsubtype_blockio | vioblockopen, | ||
269 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
270 | viopath_sourceinst(viopath_hostLp), | ||
271 | viopath_targetinst(viopath_hostLp), | ||
272 | (u64)(unsigned long)&we, VIOVERSION << 16, | ||
273 | ((u64)unit << 48) | ((u64)flags<< 32), | ||
274 | 0, 0, 0); | ||
275 | if (hvrc != 0) { | ||
276 | printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n", | ||
277 | (int)hvrc); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | wait_for_completion(&we.com); | ||
282 | |||
283 | if (we.rc != 0) { | ||
284 | if (flags != 0) | ||
285 | return; | ||
286 | /* try again with read only flag set */ | ||
287 | flags = vioblockflags_ro; | ||
288 | goto retry; | ||
289 | } | ||
290 | |||
291 | /* Send the close event to OS/400. We DON'T expect a response */ | ||
292 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
293 | HvLpEvent_Type_VirtualIo, | ||
294 | viomajorsubtype_blockio | vioblockclose, | ||
295 | HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | ||
296 | viopath_sourceinst(viopath_hostLp), | ||
297 | viopath_targetinst(viopath_hostLp), | ||
298 | 0, VIOVERSION << 16, | ||
299 | ((u64)unit << 48) | ((u64)flags << 32), | ||
300 | 0, 0, 0); | ||
301 | if (hvrc != 0) { | ||
302 | printk(KERN_WARNING "probe_disk: " | ||
303 | "bad rc sending event to OS/400 %d\n", (int)hvrc); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | ||
308 | "block", "IBM,iSeries-viodasd", NULL); | ||
309 | } | ||
310 | |||
311 | static void __init get_viodasd_info(struct device_node *vio_root) | ||
312 | { | ||
313 | int rc; | ||
314 | u32 unit; | ||
315 | |||
316 | rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2); | ||
317 | if (rc) { | ||
318 | printk(KERN_WARNING "get_viodasd_info: " | ||
319 | "error opening path to host partition %d\n", | ||
320 | viopath_hostLp); | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | /* Initialize our request handler */ | ||
325 | vio_setHandler(viomajorsubtype_blockio, handle_block_event); | ||
326 | |||
327 | for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++) | ||
328 | probe_disk(vio_root, unit); | ||
329 | |||
330 | vio_clearHandler(viomajorsubtype_blockio); | ||
331 | viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2); | ||
332 | } | ||
333 | |||
334 | static void __init handle_cd_event(struct HvLpEvent *event) | ||
335 | { | ||
336 | struct viocdlpevent *bevent; | ||
337 | struct vio_waitevent *pwe; | ||
338 | |||
339 | if (!event) | ||
340 | /* Notification that a partition went away! */ | ||
341 | return; | ||
342 | |||
343 | /* First, we should NEVER get an int here...only acks */ | ||
344 | if (hvlpevent_is_int(event)) { | ||
345 | printk(KERN_WARNING "handle_cd_event: got an unexpected int\n"); | ||
346 | if (hvlpevent_need_ack(event)) { | ||
347 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
348 | HvCallEvent_ackLpEvent(event); | ||
349 | } | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | bevent = (struct viocdlpevent *)event; | ||
354 | |||
355 | switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | ||
356 | case viocdgetinfo: | ||
357 | pwe = (struct vio_waitevent *)event->xCorrelationToken; | ||
358 | pwe->rc = event->xRc; | ||
359 | pwe->sub_result = bevent->sub_result; | ||
360 | complete(&pwe->com); | ||
361 | break; | ||
362 | |||
363 | default: | ||
364 | printk(KERN_WARNING "handle_cd_event: " | ||
365 | "message with unexpected subtype %0x04X!\n", | ||
366 | event->xSubtype & VIOMINOR_SUBTYPE_MASK); | ||
367 | if (hvlpevent_need_ack(event)) { | ||
368 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
369 | HvCallEvent_ackLpEvent(event); | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | static void __init get_viocd_info(struct device_node *vio_root) | ||
375 | { | ||
376 | HvLpEvent_Rc hvrc; | ||
377 | u32 unit; | ||
378 | struct vio_waitevent we; | ||
379 | struct vio_resource *unitinfo; | ||
380 | dma_addr_t unitinfo_dmaaddr; | ||
381 | int ret; | ||
382 | |||
383 | ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2); | ||
384 | if (ret) { | ||
385 | printk(KERN_WARNING | ||
386 | "get_viocd_info: error opening path to host partition %d\n", | ||
387 | viopath_hostLp); | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | /* Initialize our request handler */ | ||
392 | vio_setHandler(viomajorsubtype_cdio, handle_cd_event); | ||
393 | |||
394 | unitinfo = iseries_hv_alloc( | ||
395 | sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | ||
396 | &unitinfo_dmaaddr, GFP_ATOMIC); | ||
397 | if (!unitinfo) { | ||
398 | printk(KERN_WARNING | ||
399 | "get_viocd_info: error allocating unitinfo\n"); | ||
400 | goto clear_handler; | ||
401 | } | ||
402 | |||
403 | memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS); | ||
404 | |||
405 | init_completion(&we.com); | ||
406 | |||
407 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
408 | HvLpEvent_Type_VirtualIo, | ||
409 | viomajorsubtype_cdio | viocdgetinfo, | ||
410 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
411 | viopath_sourceinst(viopath_hostLp), | ||
412 | viopath_targetinst(viopath_hostLp), | ||
413 | (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0, | ||
414 | sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0); | ||
415 | if (hvrc != HvLpEvent_Rc_Good) { | ||
416 | printk(KERN_WARNING | ||
417 | "get_viocd_info: cdrom error sending event. rc %d\n", | ||
418 | (int)hvrc); | ||
419 | goto hv_free; | ||
420 | } | ||
421 | |||
422 | wait_for_completion(&we.com); | ||
423 | |||
424 | if (we.rc) { | ||
425 | printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n", | ||
426 | we.rc, we.sub_result); | ||
427 | goto hv_free; | ||
428 | } | ||
429 | |||
430 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && | ||
431 | unitinfo[unit].rsrcname[0]; unit++) { | ||
432 | if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit, | ||
433 | "block", "IBM,iSeries-viocd", &unitinfo[unit])) | ||
434 | break; | ||
435 | } | ||
436 | |||
437 | hv_free: | ||
438 | iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | ||
439 | unitinfo, unitinfo_dmaaddr); | ||
440 | clear_handler: | ||
441 | vio_clearHandler(viomajorsubtype_cdio); | ||
442 | viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2); | ||
443 | } | ||
444 | |||
445 | /* Handle interrupt events for tape */ | ||
446 | static void __init handle_tape_event(struct HvLpEvent *event) | ||
447 | { | ||
448 | struct vio_waitevent *we; | ||
449 | struct viotapelpevent *tevent = (struct viotapelpevent *)event; | ||
450 | |||
451 | if (event == NULL) | ||
452 | /* Notification that a partition went away! */ | ||
453 | return; | ||
454 | |||
455 | we = (struct vio_waitevent *)event->xCorrelationToken; | ||
456 | switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | ||
457 | case viotapegetinfo: | ||
458 | we->rc = tevent->sub_type_result; | ||
459 | complete(&we->com); | ||
460 | break; | ||
461 | default: | ||
462 | printk(KERN_WARNING "handle_tape_event: weird ack\n"); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | static void __init get_viotape_info(struct device_node *vio_root) | ||
467 | { | ||
468 | HvLpEvent_Rc hvrc; | ||
469 | u32 unit; | ||
470 | struct vio_resource *unitinfo; | ||
471 | dma_addr_t unitinfo_dmaaddr; | ||
472 | size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES; | ||
473 | struct vio_waitevent we; | ||
474 | int ret; | ||
475 | |||
476 | init_completion(&we.com); | ||
477 | |||
478 | ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2); | ||
479 | if (ret) { | ||
480 | printk(KERN_WARNING "get_viotape_info: " | ||
481 | "error on viopath_open to hostlp %d\n", ret); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | vio_setHandler(viomajorsubtype_tape, handle_tape_event); | ||
486 | |||
487 | unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC); | ||
488 | if (!unitinfo) | ||
489 | goto clear_handler; | ||
490 | |||
491 | memset(unitinfo, 0, len); | ||
492 | |||
493 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
494 | HvLpEvent_Type_VirtualIo, | ||
495 | viomajorsubtype_tape | viotapegetinfo, | ||
496 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
497 | viopath_sourceinst(viopath_hostLp), | ||
498 | viopath_targetinst(viopath_hostLp), | ||
499 | (u64)(unsigned long)&we, VIOVERSION << 16, | ||
500 | unitinfo_dmaaddr, len, 0, 0); | ||
501 | if (hvrc != HvLpEvent_Rc_Good) { | ||
502 | printk(KERN_WARNING "get_viotape_info: hv error on op %d\n", | ||
503 | (int)hvrc); | ||
504 | goto hv_free; | ||
505 | } | ||
506 | |||
507 | wait_for_completion(&we.com); | ||
508 | |||
509 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) && | ||
510 | unitinfo[unit].rsrcname[0]; unit++) { | ||
511 | if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit, | ||
512 | unit, "byte", "IBM,iSeries-viotape", | ||
513 | &unitinfo[unit])) | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | hv_free: | ||
518 | iseries_hv_free(len, unitinfo, unitinfo_dmaaddr); | ||
519 | clear_handler: | ||
520 | vio_clearHandler(viomajorsubtype_tape); | ||
521 | viopath_close(viopath_hostLp, viomajorsubtype_tape, 2); | ||
522 | } | ||
523 | |||
524 | static int __init iseries_vio_init(void) | ||
525 | { | ||
526 | struct device_node *vio_root; | ||
527 | int ret = -ENODEV; | ||
528 | |||
529 | if (!firmware_has_feature(FW_FEATURE_ISERIES)) | ||
530 | goto out; | ||
531 | |||
532 | iommu_vio_init(); | ||
533 | |||
534 | vio_root = of_find_node_by_path("/vdevice"); | ||
535 | if (!vio_root) | ||
536 | goto out; | ||
537 | |||
538 | if (viopath_hostLp == HvLpIndexInvalid) { | ||
539 | vio_set_hostlp(); | ||
540 | /* If we don't have a host, bail out */ | ||
541 | if (viopath_hostLp == HvLpIndexInvalid) | ||
542 | goto put_node; | ||
543 | } | ||
544 | |||
545 | get_viodasd_info(vio_root); | ||
546 | get_viocd_info(vio_root); | ||
547 | get_viotape_info(vio_root); | ||
548 | |||
549 | ret = 0; | ||
550 | |||
551 | put_node: | ||
552 | of_node_put(vio_root); | ||
553 | out: | ||
554 | return ret; | ||
555 | } | ||
556 | arch_initcall(iseries_vio_init); | ||