PageRenderTime 73ms CodeModel.GetById 10ms app.highlight 56ms RepoModel.GetById 0ms app.codeStats 0ms

/arch/powerpc/platforms/iseries/vio.c

https://bitbucket.org/cresqo/cm7-p500-kernel
C | 557 lines | 437 code | 74 blank | 46 comment | 58 complexity | c6b50ce068f5015087bb95f08e207fef MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.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/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
 50struct vio_waitevent {
 51	struct completion	com;
 52	int			rc;
 53	u16			sub_result;
 54};
 55
 56struct vio_resource {
 57	char	rsrcname[10];
 58	char	type[4];
 59	char	model[3];
 60};
 61
 62static 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
 78static void free_property(struct property *np)
 79{
 80	kfree(np);
 81}
 82
 83static 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
102static 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
118static 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
129static 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
140static 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 */
191struct 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}
210EXPORT_SYMBOL_GPL(vio_create_viodasd);
211
212static 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
257static 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
263retry:
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
312static 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
335static 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
375static 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 */
447static 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
467static 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	init_completion(&we.com);
478
479	ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
480	if (ret) {
481		printk(KERN_WARNING "get_viotape_info: "
482			"error on viopath_open to hostlp %d\n", ret);
483		return;
484	}
485
486	vio_setHandler(viomajorsubtype_tape, handle_tape_event);
487
488	unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
489	if (!unitinfo)
490		goto clear_handler;
491
492	memset(unitinfo, 0, len);
493
494	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
495			HvLpEvent_Type_VirtualIo,
496			viomajorsubtype_tape | viotapegetinfo,
497			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
498			viopath_sourceinst(viopath_hostLp),
499			viopath_targetinst(viopath_hostLp),
500			(u64)(unsigned long)&we, VIOVERSION << 16,
501			unitinfo_dmaaddr, len, 0, 0);
502	if (hvrc != HvLpEvent_Rc_Good) {
503		printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
504				(int)hvrc);
505		goto hv_free;
506	}
507
508	wait_for_completion(&we.com);
509
510	for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
511			unitinfo[unit].rsrcname[0]; unit++) {
512		if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
513				unit, "byte", "IBM,iSeries-viotape",
514				&unitinfo[unit]))
515			break;
516	}
517
518 hv_free:
519	iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
520 clear_handler:
521	vio_clearHandler(viomajorsubtype_tape);
522	viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
523}
524
525static int __init iseries_vio_init(void)
526{
527	struct device_node *vio_root;
528	int ret = -ENODEV;
529
530	if (!firmware_has_feature(FW_FEATURE_ISERIES))
531		goto out;
532
533	iommu_vio_init();
534
535	vio_root = of_find_node_by_path("/vdevice");
536	if (!vio_root)
537		goto out;
538
539	if (viopath_hostLp == HvLpIndexInvalid) {
540		vio_set_hostlp();
541		/* If we don't have a host, bail out */
542		if (viopath_hostLp == HvLpIndexInvalid)
543			goto put_node;
544	}
545
546	get_viodasd_info(vio_root);
547	get_viocd_info(vio_root);
548	get_viotape_info(vio_root);
549
550	ret = 0;
551
552 put_node:
553	of_node_put(vio_root);
554 out:
555	return ret;
556}
557arch_initcall(iseries_vio_init);