• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/gfp.h>
26 #include <linux/completion.h>
27 #include <linux/proc_fs.h>
28 #include <linux/module.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 
new_property(const char * name,int length,const void * value)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 
free_property(struct property * np)78 static void free_property(struct property *np)
79 {
80 	kfree(np);
81 }
82 
new_node(const char * path,struct device_node * parent)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 = kmalloc(strlen(path) + 1, GFP_KERNEL);
91 	if (!np->full_name) {
92 		kfree(np);
93 		return NULL;
94 	}
95 	strcpy(np->full_name, path);
96 	of_node_set_flag(np, OF_DYNAMIC);
97 	kref_init(&np->kref);
98 	np->parent = of_node_get(parent);
99 	return np;
100 }
101 
free_node(struct device_node * np)102 static void free_node(struct device_node *np)
103 {
104 	struct property *next;
105 	struct property *prop;
106 
107 	next = np->properties;
108 	while (next) {
109 		prop = next;
110 		next = prop->next;
111 		free_property(prop);
112 	}
113 	of_node_put(np->parent);
114 	kfree(np->full_name);
115 	kfree(np);
116 }
117 
add_string_property(struct device_node * np,const char * name,const char * value)118 static int add_string_property(struct device_node *np, const char *name,
119 		const char *value)
120 {
121 	struct property *nprop = new_property(name, strlen(value) + 1, value);
122 
123 	if (!nprop)
124 		return 0;
125 	prom_add_property(np, nprop);
126 	return 1;
127 }
128 
add_raw_property(struct device_node * np,const char * name,int length,const void * value)129 static int add_raw_property(struct device_node *np, const char *name,
130 		int length, const void *value)
131 {
132 	struct property *nprop = new_property(name, length, value);
133 
134 	if (!nprop)
135 		return 0;
136 	prom_add_property(np, nprop);
137 	return 1;
138 }
139 
do_device_node(struct device_node * parent,const char * name,u32 reg,u32 unit,const char * type,const char * compat,struct vio_resource * res)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)
143 {
144 	struct device_node *np;
145 	char path[32];
146 
147 	snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
148 	np = new_node(path, parent);
149 	if (!np)
150 		return NULL;
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), &reg) ||
155 		!add_raw_property(np, "linux,unit_address",
156 			sizeof(unit), &unit)) {
157 		goto node_free;
158 	}
159 	if (res) {
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))
166 			goto node_free;
167 	}
168 	np->name = of_get_property(np, "name", NULL);
169 	np->type = of_get_property(np, "device_type", NULL);
170 	of_attach_node(np);
171 #ifdef CONFIG_PROC_DEVICETREE
172 	if (parent->pde) {
173 		struct proc_dir_entry *ent;
174 
175 		ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
176 		if (ent)
177 			proc_device_tree_add_node(np, ent);
178 	}
179 #endif
180 	return np;
181 
182  node_free:
183 	free_node(np);
184 	return NULL;
185 }
186 
187 /*
188  * This is here so that we can dynamically add viodasd
189  * devices without exposing all the above infrastructure.
190  */
vio_create_viodasd(u32 unit)191 struct vio_dev *vio_create_viodasd(u32 unit)
192 {
193 	struct device_node *vio_root;
194 	struct device_node *np;
195 	struct vio_dev *vdev = NULL;
196 
197 	vio_root = of_find_node_by_path("/vdevice");
198 	if (!vio_root)
199 		return NULL;
200 	np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
201 			"block", "IBM,iSeries-viodasd", NULL);
202 	of_node_put(vio_root);
203 	if (np) {
204 		vdev = vio_register_device_node(np);
205 		if (!vdev)
206 			free_node(np);
207 	}
208 	return vdev;
209 }
210 EXPORT_SYMBOL_GPL(vio_create_viodasd);
211 
handle_block_event(struct HvLpEvent * event)212 static void __init handle_block_event(struct HvLpEvent *event)
213 {
214 	struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
215 	struct vio_waitevent *pwe;
216 
217 	if (event == NULL)
218 		/* Notification that a partition went away! */
219 		return;
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);
227 		}
228 		return;
229 	}
230 
231 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
232 	case vioblockopen:
233 		/*
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
240 		 */
241 		pwe = (struct vio_waitevent *)event->xCorrelationToken;
242 		pwe->rc = event->xRc;
243 		pwe->sub_result = bevent->sub_result;
244 		complete(&pwe->com);
245 		break;
246 	case vioblockclose:
247 		break;
248 	default:
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);
253 		}
254 	}
255 }
256 
probe_disk(struct device_node * vio_root,u32 unit)257 static void __init probe_disk(struct device_node *vio_root, u32 unit)
258 {
259 	HvLpEvent_Rc hvrc;
260 	struct vio_waitevent we;
261 	u16 flags = 0;
262 
263 retry:
264 	init_completion(&we.com);
265 
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),
275 			0, 0, 0);
276 	if (hvrc != 0) {
277 		printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
278 			(int)hvrc);
279 		return;
280 	}
281 
282 	wait_for_completion(&we.com);
283 
284 	if (we.rc != 0) {
285 		if (flags != 0)
286 			return;
287 		/* try again with read only flag set */
288 		flags = vioblockflags_ro;
289 		goto retry;
290 	}
291 
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),
299 			0, VIOVERSION << 16,
300 			((u64)unit << 48) | ((u64)flags << 32),
301 			0, 0, 0);
302 	if (hvrc != 0) {
303 		printk(KERN_WARNING "probe_disk: "
304 		       "bad rc sending event to OS/400 %d\n", (int)hvrc);
305 		return;
306 	}
307 
308 	do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
309 			"block", "IBM,iSeries-viodasd", NULL);
310 }
311 
get_viodasd_info(struct device_node * vio_root)312 static void __init get_viodasd_info(struct device_node *vio_root)
313 {
314 	int rc;
315 	u32 unit;
316 
317 	rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
318 	if (rc) {
319 		printk(KERN_WARNING "get_viodasd_info: "
320 		       "error opening path to host partition %d\n",
321 		       viopath_hostLp);
322 		return;
323 	}
324 
325 	/* Initialize our request handler */
326 	vio_setHandler(viomajorsubtype_blockio, handle_block_event);
327 
328 	for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
329 		probe_disk(vio_root, unit);
330 
331 	vio_clearHandler(viomajorsubtype_blockio);
332 	viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
333 }
334 
handle_cd_event(struct HvLpEvent * event)335 static void __init handle_cd_event(struct HvLpEvent *event)
336 {
337 	struct viocdlpevent *bevent;
338 	struct vio_waitevent *pwe;
339 
340 	if (!event)
341 		/* Notification that a partition went away! */
342 		return;
343 
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);
350 		}
351 		return;
352 	}
353 
354 	bevent = (struct viocdlpevent *)event;
355 
356 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
357 	case viocdgetinfo:
358 		pwe = (struct vio_waitevent *)event->xCorrelationToken;
359 		pwe->rc = event->xRc;
360 		pwe->sub_result = bevent->sub_result;
361 		complete(&pwe->com);
362 		break;
363 
364 	default:
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);
371 		}
372 	}
373 }
374 
get_viocd_info(struct device_node * vio_root)375 static void __init get_viocd_info(struct device_node *vio_root)
376 {
377 	HvLpEvent_Rc hvrc;
378 	u32 unit;
379 	struct vio_waitevent we;
380 	struct vio_resource *unitinfo;
381 	dma_addr_t unitinfo_dmaaddr;
382 	int ret;
383 
384 	ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
385 	if (ret) {
386 		printk(KERN_WARNING
387 			"get_viocd_info: error opening path to host partition %d\n",
388 			viopath_hostLp);
389 		return;
390 	}
391 
392 	/* Initialize our request handler */
393 	vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
394 
395 	unitinfo = iseries_hv_alloc(
396 			sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
397 			&unitinfo_dmaaddr, GFP_ATOMIC);
398 	if (!unitinfo) {
399 		printk(KERN_WARNING
400 			"get_viocd_info: error allocating unitinfo\n");
401 		goto clear_handler;
402 	}
403 
404 	memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
405 
406 	init_completion(&we.com);
407 
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) {
417 		printk(KERN_WARNING
418 			"get_viocd_info: cdrom error sending event. rc %d\n",
419 			(int)hvrc);
420 		goto hv_free;
421 	}
422 
423 	wait_for_completion(&we.com);
424 
425 	if (we.rc) {
426 		printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
427 			we.rc, we.sub_result);
428 		goto hv_free;
429 	}
430 
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]))
435 			break;
436 	}
437 
438  hv_free:
439 	iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
440 			unitinfo, unitinfo_dmaaddr);
441  clear_handler:
442 	vio_clearHandler(viomajorsubtype_cdio);
443 	viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
444 }
445 
446 /* Handle interrupt events for tape */
handle_tape_event(struct HvLpEvent * event)447 static void __init handle_tape_event(struct HvLpEvent *event)
448 {
449 	struct vio_waitevent *we;
450 	struct viotapelpevent *tevent = (struct viotapelpevent *)event;
451 
452 	if (event == NULL)
453 		/* Notification that a partition went away! */
454 		return;
455 
456 	we = (struct vio_waitevent *)event->xCorrelationToken;
457 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
458 	case viotapegetinfo:
459 		we->rc = tevent->sub_type_result;
460 		complete(&we->com);
461 		break;
462 	default:
463 		printk(KERN_WARNING "handle_tape_event: weird ack\n");
464 	}
465 }
466 
get_viotape_info(struct device_node * vio_root)467 static void __init get_viotape_info(struct device_node *vio_root)
468 {
469 	HvLpEvent_Rc hvrc;
470 	u32 unit;
471 	struct vio_resource *unitinfo;
472 	dma_addr_t unitinfo_dmaaddr;
473 	size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
474 	struct vio_waitevent we;
475 	int ret;
476 
477 	ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
478 	if (ret) {
479 		printk(KERN_WARNING "get_viotape_info: "
480 			"error on viopath_open to hostlp %d\n", ret);
481 		return;
482 	}
483 
484 	vio_setHandler(viomajorsubtype_tape, handle_tape_event);
485 
486 	unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
487 	if (!unitinfo)
488 		goto clear_handler;
489 
490 	memset(unitinfo, 0, len);
491 
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",
502 				(int)hvrc);
503 		goto hv_free;
504 	}
505 
506 	wait_for_completion(&we.com);
507 
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",
512 				&unitinfo[unit]))
513 			break;
514 	}
515 
516  hv_free:
517 	iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
518  clear_handler:
519 	vio_clearHandler(viomajorsubtype_tape);
520 	viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
521 }
522 
iseries_vio_init(void)523 static int __init iseries_vio_init(void)
524 {
525 	struct device_node *vio_root;
526 	int ret = -ENODEV;
527 
528 	if (!firmware_has_feature(FW_FEATURE_ISERIES))
529 		goto out;
530 
531 	iommu_vio_init();
532 
533 	vio_root = of_find_node_by_path("/vdevice");
534 	if (!vio_root)
535 		goto out;
536 
537 	if (viopath_hostLp == HvLpIndexInvalid) {
538 		vio_set_hostlp();
539 		/* If we don't have a host, bail out */
540 		if (viopath_hostLp == HvLpIndexInvalid)
541 			goto put_node;
542 	}
543 
544 	get_viodasd_info(vio_root);
545 	get_viocd_info(vio_root);
546 	get_viotape_info(vio_root);
547 
548 	ret = 0;
549 
550  put_node:
551 	of_node_put(vio_root);
552  out:
553 	return ret;
554 }
555 arch_initcall(iseries_vio_init);
556