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>
29 #include <asm/firmware.h>
30 #include <asm/iseries/vio.h>
31 #include <asm/iseries/iommu.h>
32 #include <asm/iseries/hv_types.h>
33 #include <asm/iseries/hv_lp_event.h>
37 #define FIRST_VSCSI (FIRST_VTY + NUM_VTYS)
39 #define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS)
40 #define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS
41 #define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS)
42 #define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS
43 #define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS)
44 #define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS
45 #define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS)
46 #define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES
48 struct vio_waitevent {
49 struct completion com;
60 static struct property * __init new_property(const char *name, int length,
63 struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
68 np->name = (char *)(np + 1);
69 np->value = np->name + strlen(name) + 1;
70 strcpy(np->name, name);
71 memcpy(np->value, value, length);
76 static void __init free_property(struct property *np)
81 static struct device_node * __init new_node(const char *path,
82 struct device_node *parent)
84 struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
88 np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
93 strcpy(np->full_name, path);
94 of_node_set_flag(np, OF_DYNAMIC);
96 np->parent = of_node_get(parent);
100 static void __init free_node(struct device_node *np)
102 struct property *next;
103 struct property *prop;
105 next = np->properties;
111 of_node_put(np->parent);
112 kfree(np->full_name);
116 static int __init add_string_property(struct device_node *np, const char *name,
119 struct property *nprop = new_property(name, strlen(value) + 1, value);
123 prom_add_property(np, nprop);
127 static int __init add_raw_property(struct device_node *np, const char *name,
128 int length, const void *value)
130 struct property *nprop = new_property(name, length, value);
134 prom_add_property(np, nprop);
138 static void __init handle_cd_event(struct HvLpEvent *event)
140 struct viocdlpevent *bevent;
141 struct vio_waitevent *pwe;
144 /* Notification that a partition went away! */
147 /* First, we should NEVER get an int here...only acks */
148 if (hvlpevent_is_int(event)) {
149 printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
150 if (hvlpevent_need_ack(event)) {
151 event->xRc = HvLpEvent_Rc_InvalidSubtype;
152 HvCallEvent_ackLpEvent(event);
157 bevent = (struct viocdlpevent *)event;
159 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
161 pwe = (struct vio_waitevent *)event->xCorrelationToken;
162 pwe->rc = event->xRc;
163 pwe->sub_result = bevent->sub_result;
168 printk(KERN_WARNING "handle_cd_event: "
169 "message with unexpected subtype %0x04X!\n",
170 event->xSubtype & VIOMINOR_SUBTYPE_MASK);
171 if (hvlpevent_need_ack(event)) {
172 event->xRc = HvLpEvent_Rc_InvalidSubtype;
173 HvCallEvent_ackLpEvent(event);
178 static void __init get_viocd_info(struct device_node *vio_root)
182 struct vio_waitevent we;
183 struct vio_resource *unitinfo;
184 dma_addr_t unitinfo_dmaaddr;
187 ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
190 "get_viocd_info: error opening path to host partition %d\n",
195 /* Initialize our request handler */
196 vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
198 unitinfo = iseries_hv_alloc(
199 sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
200 &unitinfo_dmaaddr, GFP_ATOMIC);
203 "get_viocd_info: error allocating unitinfo\n");
207 memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
209 init_completion(&we.com);
211 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
212 HvLpEvent_Type_VirtualIo,
213 viomajorsubtype_cdio | viocdgetinfo,
214 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
215 viopath_sourceinst(viopath_hostLp),
216 viopath_targetinst(viopath_hostLp),
217 (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
218 sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
219 if (hvrc != HvLpEvent_Rc_Good) {
221 "get_viocd_info: cdrom error sending event. rc %d\n",
226 wait_for_completion(&we.com);
229 printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
230 we.rc, we.sub_result);
234 for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
235 unitinfo[unit].rsrcname[0]; unit++) {
236 struct device_node *np;
238 u32 reg = FIRST_VIOCD + unit;
240 snprintf(name, sizeof(name), "/vdevice/viocd@%08x", reg);
241 np = new_node(name, vio_root);
244 if (!add_string_property(np, "name", "viocd") ||
245 !add_string_property(np, "device_type", "block") ||
246 !add_string_property(np, "compatible",
247 "IBM,iSeries-viocd") ||
248 !add_raw_property(np, "reg", sizeof(reg), ®) ||
249 !add_raw_property(np, "linux,unit_address",
250 sizeof(unit), &unit) ||
251 !add_raw_property(np, "linux,vio_rsrcname",
252 sizeof(unitinfo[unit].rsrcname),
253 unitinfo[unit].rsrcname) ||
254 !add_raw_property(np, "linux,vio_type",
255 sizeof(unitinfo[unit].type),
256 unitinfo[unit].type) ||
257 !add_raw_property(np, "linux,vio_model",
258 sizeof(unitinfo[unit].model),
259 unitinfo[unit].model))
261 np->name = of_get_property(np, "name", NULL);
262 np->type = of_get_property(np, "device_type", NULL);
264 #ifdef CONFIG_PROC_DEVICETREE
266 struct proc_dir_entry *ent;
268 ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
271 proc_device_tree_add_node(np, ent);
282 iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
283 unitinfo, unitinfo_dmaaddr);
285 vio_clearHandler(viomajorsubtype_cdio);
286 viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
289 /* Handle interrupt events for tape */
290 static void __init handle_tape_event(struct HvLpEvent *event)
292 struct vio_waitevent *we;
293 struct viotapelpevent *tevent = (struct viotapelpevent *)event;
296 /* Notification that a partition went away! */
299 we = (struct vio_waitevent *)event->xCorrelationToken;
300 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
302 we->rc = tevent->sub_type_result;
306 printk(KERN_WARNING "handle_tape_event: weird ack\n");
310 static void __init get_viotape_info(struct device_node *vio_root)
314 struct vio_resource *unitinfo;
315 dma_addr_t unitinfo_dmaaddr;
316 size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
317 struct vio_waitevent we;
320 ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
322 printk(KERN_WARNING "get_viotape_info: "
323 "error on viopath_open to hostlp %d\n", ret);
327 vio_setHandler(viomajorsubtype_tape, handle_tape_event);
329 unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
333 memset(unitinfo, 0, len);
335 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
336 HvLpEvent_Type_VirtualIo,
337 viomajorsubtype_tape | viotapegetinfo,
338 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
339 viopath_sourceinst(viopath_hostLp),
340 viopath_targetinst(viopath_hostLp),
341 (u64)(unsigned long)&we, VIOVERSION << 16,
342 unitinfo_dmaaddr, len, 0, 0);
343 if (hvrc != HvLpEvent_Rc_Good) {
344 printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
349 wait_for_completion(&we.com);
351 for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
352 unitinfo[unit].rsrcname[0]; unit++) {
353 struct device_node *np;
355 u32 reg = FIRST_VIOTAPE + unit;
357 snprintf(name, sizeof(name), "/vdevice/viotape@%08x", reg);
358 np = new_node(name, vio_root);
361 if (!add_string_property(np, "name", "viotape") ||
362 !add_string_property(np, "device_type", "byte") ||
363 !add_string_property(np, "compatible",
364 "IBM,iSeries-viotape") ||
365 !add_raw_property(np, "reg", sizeof(reg), ®) ||
366 !add_raw_property(np, "linux,unit_address",
367 sizeof(unit), &unit) ||
368 !add_raw_property(np, "linux,vio_rsrcname",
369 sizeof(unitinfo[unit].rsrcname),
370 unitinfo[unit].rsrcname) ||
371 !add_raw_property(np, "linux,vio_type",
372 sizeof(unitinfo[unit].type),
373 unitinfo[unit].type) ||
374 !add_raw_property(np, "linux,vio_model",
375 sizeof(unitinfo[unit].model),
376 unitinfo[unit].model))
378 np->name = of_get_property(np, "name", NULL);
379 np->type = of_get_property(np, "device_type", NULL);
381 #ifdef CONFIG_PROC_DEVICETREE
383 struct proc_dir_entry *ent;
385 ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
388 proc_device_tree_add_node(np, ent);
399 iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
401 vio_clearHandler(viomajorsubtype_tape);
402 viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
405 static int __init iseries_vio_init(void)
407 struct device_node *vio_root;
409 if (!firmware_has_feature(FW_FEATURE_ISERIES))
414 vio_root = of_find_node_by_path("/vdevice");
418 if (viopath_hostLp == HvLpIndexInvalid) {
420 /* If we don't have a host, bail out */
421 if (viopath_hostLp == HvLpIndexInvalid)
425 get_viocd_info(vio_root);
426 get_viotape_info(vio_root);
431 of_node_put(vio_root);
434 arch_initcall(iseries_vio_init);