2 * Legacy iSeries specific vio initialisation
3 * that needs to be built in (not a module).
5 * © Copyright 2007 IBM Corporation
6 * Author: Stephen Rothwell
7 * Some parts collected from various other files
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.
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.
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
24 #include <linux/init.h>
25 #include <linux/gfp.h>
26 #include <linux/completion.h>
27 #include <linux/proc_fs.h>
28 #include <linux/module.h>
30 #include <asm/firmware.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>
39 #define FIRST_VSCSI (FIRST_VTY + NUM_VTYS)
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
50 struct vio_waitevent
{
51 struct completion com
;
62 static struct property
*new_property(const char *name
, int length
,
65 struct property
*np
= kzalloc(sizeof(*np
) + strlen(name
) + 1 + length
,
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
);
78 static void free_property(struct property
*np
)
83 static struct device_node
*new_node(const char *path
,
84 struct device_node
*parent
)
86 struct device_node
*np
= kzalloc(sizeof(*np
), GFP_KERNEL
);
90 np
->full_name
= kmalloc(strlen(path
) + 1, GFP_KERNEL
);
95 strcpy(np
->full_name
, path
);
96 of_node_set_flag(np
, OF_DYNAMIC
);
98 np
->parent
= of_node_get(parent
);
102 static void free_node(struct device_node
*np
)
104 struct property
*next
;
105 struct property
*prop
;
107 next
= np
->properties
;
113 of_node_put(np
->parent
);
114 kfree(np
->full_name
);
118 static int add_string_property(struct device_node
*np
, const char *name
,
121 struct property
*nprop
= new_property(name
, strlen(value
) + 1, value
);
125 prom_add_property(np
, nprop
);
129 static int add_raw_property(struct device_node
*np
, const char *name
,
130 int length
, const void *value
)
132 struct property
*nprop
= new_property(name
, length
, value
);
136 prom_add_property(np
, nprop
);
140 static struct device_node
*do_device_node(struct device_node
*parent
,
141 const char *name
, u32 reg
, u32 unit
, const char *type
,
142 const char *compat
, struct vio_resource
*res
)
144 struct device_node
*np
;
147 snprintf(path
, sizeof(path
), "/vdevice/%s@%08x", name
, reg
);
148 np
= new_node(path
, parent
);
151 if (!add_string_property(np
, "name", name
) ||
152 !add_string_property(np
, "device_type", type
) ||
153 !add_string_property(np
, "compatible", compat
) ||
154 !add_raw_property(np
, "reg", sizeof(reg
), ®
) ||
155 !add_raw_property(np
, "linux,unit_address",
156 sizeof(unit
), &unit
)) {
160 if (!add_raw_property(np
, "linux,vio_rsrcname",
161 sizeof(res
->rsrcname
), res
->rsrcname
) ||
162 !add_raw_property(np
, "linux,vio_type",
163 sizeof(res
->type
), res
->type
) ||
164 !add_raw_property(np
, "linux,vio_model",
165 sizeof(res
->model
), res
->model
))
168 np
->name
= of_get_property(np
, "name", NULL
);
169 np
->type
= of_get_property(np
, "device_type", NULL
);
171 #ifdef CONFIG_PROC_DEVICETREE
173 struct proc_dir_entry
*ent
;
175 ent
= proc_mkdir(strrchr(np
->full_name
, '/') + 1, parent
->pde
);
177 proc_device_tree_add_node(np
, ent
);
188 * This is here so that we can dynamically add viodasd
189 * devices without exposing all the above infrastructure.
191 struct vio_dev
*vio_create_viodasd(u32 unit
)
193 struct device_node
*vio_root
;
194 struct device_node
*np
;
195 struct vio_dev
*vdev
= NULL
;
197 vio_root
= of_find_node_by_path("/vdevice");
200 np
= do_device_node(vio_root
, "viodasd", FIRST_VIODASD
+ unit
, unit
,
201 "block", "IBM,iSeries-viodasd", NULL
);
202 of_node_put(vio_root
);
204 vdev
= vio_register_device_node(np
);
210 EXPORT_SYMBOL_GPL(vio_create_viodasd
);
212 static void __init
handle_block_event(struct HvLpEvent
*event
)
214 struct vioblocklpevent
*bevent
= (struct vioblocklpevent
*)event
;
215 struct vio_waitevent
*pwe
;
218 /* Notification that a partition went away! */
220 /* First, we should NEVER get an int here...only acks */
221 if (hvlpevent_is_int(event
)) {
222 printk(KERN_WARNING
"handle_viod_request: "
223 "Yikes! got an int in viodasd event handler!\n");
224 if (hvlpevent_need_ack(event
)) {
225 event
->xRc
= HvLpEvent_Rc_InvalidSubtype
;
226 HvCallEvent_ackLpEvent(event
);
231 switch (event
->xSubtype
& VIOMINOR_SUBTYPE_MASK
) {
234 * Handle a response to an open request. We get all the
235 * disk information in the response, so update it. The
236 * correlation token contains a pointer to a waitevent
237 * structure that has a completion in it. update the
238 * return code in the waitevent structure and post the
239 * completion to wake up the guy who sent the request
241 pwe
= (struct vio_waitevent
*)event
->xCorrelationToken
;
242 pwe
->rc
= event
->xRc
;
243 pwe
->sub_result
= bevent
->sub_result
;
249 printk(KERN_WARNING
"handle_viod_request: unexpected subtype!");
250 if (hvlpevent_need_ack(event
)) {
251 event
->xRc
= HvLpEvent_Rc_InvalidSubtype
;
252 HvCallEvent_ackLpEvent(event
);
257 static void __init
probe_disk(struct device_node
*vio_root
, u32 unit
)
260 struct vio_waitevent we
;
264 init_completion(&we
.com
);
266 /* Send the open event to OS/400 */
267 hvrc
= HvCallEvent_signalLpEventFast(viopath_hostLp
,
268 HvLpEvent_Type_VirtualIo
,
269 viomajorsubtype_blockio
| vioblockopen
,
270 HvLpEvent_AckInd_DoAck
, HvLpEvent_AckType_ImmediateAck
,
271 viopath_sourceinst(viopath_hostLp
),
272 viopath_targetinst(viopath_hostLp
),
273 (u64
)(unsigned long)&we
, VIOVERSION
<< 16,
274 ((u64
)unit
<< 48) | ((u64
)flags
<< 32),
277 printk(KERN_WARNING
"probe_disk: bad rc on HV open %d\n",
282 wait_for_completion(&we
.com
);
287 /* try again with read only flag set */
288 flags
= vioblockflags_ro
;
292 /* Send the close event to OS/400. We DON'T expect a response */
293 hvrc
= HvCallEvent_signalLpEventFast(viopath_hostLp
,
294 HvLpEvent_Type_VirtualIo
,
295 viomajorsubtype_blockio
| vioblockclose
,
296 HvLpEvent_AckInd_NoAck
, HvLpEvent_AckType_ImmediateAck
,
297 viopath_sourceinst(viopath_hostLp
),
298 viopath_targetinst(viopath_hostLp
),
300 ((u64
)unit
<< 48) | ((u64
)flags
<< 32),
303 printk(KERN_WARNING
"probe_disk: "
304 "bad rc sending event to OS/400 %d\n", (int)hvrc
);
308 do_device_node(vio_root
, "viodasd", FIRST_VIODASD
+ unit
, unit
,
309 "block", "IBM,iSeries-viodasd", NULL
);
312 static void __init
get_viodasd_info(struct device_node
*vio_root
)
317 rc
= viopath_open(viopath_hostLp
, viomajorsubtype_blockio
, 2);
319 printk(KERN_WARNING
"get_viodasd_info: "
320 "error opening path to host partition %d\n",
325 /* Initialize our request handler */
326 vio_setHandler(viomajorsubtype_blockio
, handle_block_event
);
328 for (unit
= 0; unit
< HVMAXARCHITECTEDVIRTUALDISKS
; unit
++)
329 probe_disk(vio_root
, unit
);
331 vio_clearHandler(viomajorsubtype_blockio
);
332 viopath_close(viopath_hostLp
, viomajorsubtype_blockio
, 2);
335 static void __init
handle_cd_event(struct HvLpEvent
*event
)
337 struct viocdlpevent
*bevent
;
338 struct vio_waitevent
*pwe
;
341 /* Notification that a partition went away! */
344 /* First, we should NEVER get an int here...only acks */
345 if (hvlpevent_is_int(event
)) {
346 printk(KERN_WARNING
"handle_cd_event: got an unexpected int\n");
347 if (hvlpevent_need_ack(event
)) {
348 event
->xRc
= HvLpEvent_Rc_InvalidSubtype
;
349 HvCallEvent_ackLpEvent(event
);
354 bevent
= (struct viocdlpevent
*)event
;
356 switch (event
->xSubtype
& VIOMINOR_SUBTYPE_MASK
) {
358 pwe
= (struct vio_waitevent
*)event
->xCorrelationToken
;
359 pwe
->rc
= event
->xRc
;
360 pwe
->sub_result
= bevent
->sub_result
;
365 printk(KERN_WARNING
"handle_cd_event: "
366 "message with unexpected subtype %0x04X!\n",
367 event
->xSubtype
& VIOMINOR_SUBTYPE_MASK
);
368 if (hvlpevent_need_ack(event
)) {
369 event
->xRc
= HvLpEvent_Rc_InvalidSubtype
;
370 HvCallEvent_ackLpEvent(event
);
375 static void __init
get_viocd_info(struct device_node
*vio_root
)
379 struct vio_waitevent we
;
380 struct vio_resource
*unitinfo
;
381 dma_addr_t unitinfo_dmaaddr
;
384 ret
= viopath_open(viopath_hostLp
, viomajorsubtype_cdio
, 2);
387 "get_viocd_info: error opening path to host partition %d\n",
392 /* Initialize our request handler */
393 vio_setHandler(viomajorsubtype_cdio
, handle_cd_event
);
395 unitinfo
= iseries_hv_alloc(
396 sizeof(*unitinfo
) * HVMAXARCHITECTEDVIRTUALCDROMS
,
397 &unitinfo_dmaaddr
, GFP_ATOMIC
);
400 "get_viocd_info: error allocating unitinfo\n");
404 memset(unitinfo
, 0, sizeof(*unitinfo
) * HVMAXARCHITECTEDVIRTUALCDROMS
);
406 init_completion(&we
.com
);
408 hvrc
= HvCallEvent_signalLpEventFast(viopath_hostLp
,
409 HvLpEvent_Type_VirtualIo
,
410 viomajorsubtype_cdio
| viocdgetinfo
,
411 HvLpEvent_AckInd_DoAck
, HvLpEvent_AckType_ImmediateAck
,
412 viopath_sourceinst(viopath_hostLp
),
413 viopath_targetinst(viopath_hostLp
),
414 (u64
)&we
, VIOVERSION
<< 16, unitinfo_dmaaddr
, 0,
415 sizeof(*unitinfo
) * HVMAXARCHITECTEDVIRTUALCDROMS
, 0);
416 if (hvrc
!= HvLpEvent_Rc_Good
) {
418 "get_viocd_info: cdrom error sending event. rc %d\n",
423 wait_for_completion(&we
.com
);
426 printk(KERN_WARNING
"get_viocd_info: bad rc %d:0x%04X\n",
427 we
.rc
, we
.sub_result
);
431 for (unit
= 0; (unit
< HVMAXARCHITECTEDVIRTUALCDROMS
) &&
432 unitinfo
[unit
].rsrcname
[0]; unit
++) {
433 if (!do_device_node(vio_root
, "viocd", FIRST_VIOCD
+ unit
, unit
,
434 "block", "IBM,iSeries-viocd", &unitinfo
[unit
]))
439 iseries_hv_free(sizeof(*unitinfo
) * HVMAXARCHITECTEDVIRTUALCDROMS
,
440 unitinfo
, unitinfo_dmaaddr
);
442 vio_clearHandler(viomajorsubtype_cdio
);
443 viopath_close(viopath_hostLp
, viomajorsubtype_cdio
, 2);
446 /* Handle interrupt events for tape */
447 static void __init
handle_tape_event(struct HvLpEvent
*event
)
449 struct vio_waitevent
*we
;
450 struct viotapelpevent
*tevent
= (struct viotapelpevent
*)event
;
453 /* Notification that a partition went away! */
456 we
= (struct vio_waitevent
*)event
->xCorrelationToken
;
457 switch (event
->xSubtype
& VIOMINOR_SUBTYPE_MASK
) {
459 we
->rc
= tevent
->sub_type_result
;
463 printk(KERN_WARNING
"handle_tape_event: weird ack\n");
467 static void __init
get_viotape_info(struct device_node
*vio_root
)
471 struct vio_resource
*unitinfo
;
472 dma_addr_t unitinfo_dmaaddr
;
473 size_t len
= sizeof(*unitinfo
) * HVMAXARCHITECTEDVIRTUALTAPES
;
474 struct vio_waitevent we
;
477 ret
= viopath_open(viopath_hostLp
, viomajorsubtype_tape
, 2);
479 printk(KERN_WARNING
"get_viotape_info: "
480 "error on viopath_open to hostlp %d\n", ret
);
484 vio_setHandler(viomajorsubtype_tape
, handle_tape_event
);
486 unitinfo
= iseries_hv_alloc(len
, &unitinfo_dmaaddr
, GFP_ATOMIC
);
490 memset(unitinfo
, 0, len
);
492 hvrc
= HvCallEvent_signalLpEventFast(viopath_hostLp
,
493 HvLpEvent_Type_VirtualIo
,
494 viomajorsubtype_tape
| viotapegetinfo
,
495 HvLpEvent_AckInd_DoAck
, HvLpEvent_AckType_ImmediateAck
,
496 viopath_sourceinst(viopath_hostLp
),
497 viopath_targetinst(viopath_hostLp
),
498 (u64
)(unsigned long)&we
, VIOVERSION
<< 16,
499 unitinfo_dmaaddr
, len
, 0, 0);
500 if (hvrc
!= HvLpEvent_Rc_Good
) {
501 printk(KERN_WARNING
"get_viotape_info: hv error on op %d\n",
506 wait_for_completion(&we
.com
);
508 for (unit
= 0; (unit
< HVMAXARCHITECTEDVIRTUALTAPES
) &&
509 unitinfo
[unit
].rsrcname
[0]; unit
++) {
510 if (!do_device_node(vio_root
, "viotape", FIRST_VIOTAPE
+ unit
,
511 unit
, "byte", "IBM,iSeries-viotape",
517 iseries_hv_free(len
, unitinfo
, unitinfo_dmaaddr
);
519 vio_clearHandler(viomajorsubtype_tape
);
520 viopath_close(viopath_hostLp
, viomajorsubtype_tape
, 2);
523 static int __init
iseries_vio_init(void)
525 struct device_node
*vio_root
;
528 if (!firmware_has_feature(FW_FEATURE_ISERIES
))
533 vio_root
= of_find_node_by_path("/vdevice");
537 if (viopath_hostLp
== HvLpIndexInvalid
) {
539 /* If we don't have a host, bail out */
540 if (viopath_hostLp
== HvLpIndexInvalid
)
544 get_viodasd_info(vio_root
);
545 get_viocd_info(vio_root
);
546 get_viotape_info(vio_root
);
551 of_node_put(vio_root
);
555 arch_initcall(iseries_vio_init
);