| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Legacy iSeries specific vio initialisation | 
					
						
							|  |  |  |  * that needs to be built in (not a module). | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-10-12 15:37:32 +10:00
										 |  |  |  * © Copyright 2007 IBM Corporation | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  |  *	Author: Stephen Rothwell | 
					
						
							|  |  |  |  *	Some parts collected from various other files | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software;  you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation; either version 2 of the | 
					
						
							|  |  |  |  * License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  * General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  |  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/of.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/gfp.h>
 | 
					
						
							|  |  |  | #include <linux/completion.h>
 | 
					
						
							|  |  |  | #include <linux/proc_fs.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/firmware.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | #include <asm/vio.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | #include <asm/iseries/vio.h>
 | 
					
						
							|  |  |  | #include <asm/iseries/iommu.h>
 | 
					
						
							|  |  |  | #include <asm/iseries/hv_types.h>
 | 
					
						
							|  |  |  | #include <asm/iseries/hv_lp_event.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define FIRST_VTY	0
 | 
					
						
							|  |  |  | #define NUM_VTYS	1
 | 
					
						
							|  |  |  | #define FIRST_VSCSI	(FIRST_VTY + NUM_VTYS)
 | 
					
						
							|  |  |  | #define NUM_VSCSIS	1
 | 
					
						
							|  |  |  | #define FIRST_VLAN	(FIRST_VSCSI + NUM_VSCSIS)
 | 
					
						
							|  |  |  | #define NUM_VLANS	HVMAXARCHITECTEDVIRTUALLANS
 | 
					
						
							|  |  |  | #define FIRST_VIODASD	(FIRST_VLAN + NUM_VLANS)
 | 
					
						
							|  |  |  | #define NUM_VIODASDS	HVMAXARCHITECTEDVIRTUALDISKS
 | 
					
						
							|  |  |  | #define FIRST_VIOCD	(FIRST_VIODASD + NUM_VIODASDS)
 | 
					
						
							|  |  |  | #define NUM_VIOCDS	HVMAXARCHITECTEDVIRTUALCDROMS
 | 
					
						
							|  |  |  | #define FIRST_VIOTAPE	(FIRST_VIOCD + NUM_VIOCDS)
 | 
					
						
							|  |  |  | #define NUM_VIOTAPES	HVMAXARCHITECTEDVIRTUALTAPES
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | struct vio_waitevent { | 
					
						
							|  |  |  | 	struct completion	com; | 
					
						
							|  |  |  | 	int			rc; | 
					
						
							|  |  |  | 	u16			sub_result; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct vio_resource { | 
					
						
							|  |  |  | 	char	rsrcname[10]; | 
					
						
							|  |  |  | 	char	type[4]; | 
					
						
							|  |  |  | 	char	model[3]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static struct property *new_property(const char *name, int length, | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 		const void *value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, | 
					
						
							|  |  |  | 			GFP_KERNEL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!np) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	np->name = (char *)(np + 1); | 
					
						
							|  |  |  | 	np->value = np->name + strlen(name) + 1; | 
					
						
							|  |  |  | 	strcpy(np->name, name); | 
					
						
							|  |  |  | 	memcpy(np->value, value, length); | 
					
						
							|  |  |  | 	np->length = length; | 
					
						
							|  |  |  | 	return np; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-14 08:30:55 +11:00
										 |  |  | static void free_property(struct property *np) | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | { | 
					
						
							|  |  |  | 	kfree(np); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static struct device_node *new_node(const char *path, | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 		struct device_node *parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!np) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!np->full_name) { | 
					
						
							|  |  |  | 		kfree(np); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	strcpy(np->full_name, path); | 
					
						
							|  |  |  | 	of_node_set_flag(np, OF_DYNAMIC); | 
					
						
							|  |  |  | 	kref_init(&np->kref); | 
					
						
							|  |  |  | 	np->parent = of_node_get(parent); | 
					
						
							|  |  |  | 	return np; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static void free_node(struct device_node *np) | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct property *next; | 
					
						
							|  |  |  | 	struct property *prop; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	next = np->properties; | 
					
						
							|  |  |  | 	while (next) { | 
					
						
							|  |  |  | 		prop = next; | 
					
						
							|  |  |  | 		next = prop->next; | 
					
						
							|  |  |  | 		free_property(prop); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	of_node_put(np->parent); | 
					
						
							|  |  |  | 	kfree(np->full_name); | 
					
						
							|  |  |  | 	kfree(np); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static int add_string_property(struct device_node *np, const char *name, | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 		const char *value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct property *nprop = new_property(name, strlen(value) + 1, value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!nprop) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	prom_add_property(np, nprop); | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static int add_raw_property(struct device_node *np, const char *name, | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 		int length, const void *value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct property *nprop = new_property(name, length, value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!nprop) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	prom_add_property(np, nprop); | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | static struct device_node *do_device_node(struct device_node *parent, | 
					
						
							|  |  |  | 		const char *name, u32 reg, u32 unit, const char *type, | 
					
						
							|  |  |  | 		const char *compat, struct vio_resource *res) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 	char path[32]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg); | 
					
						
							|  |  |  | 	np = new_node(path, parent); | 
					
						
							|  |  |  | 	if (!np) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	if (!add_string_property(np, "name", name) || | 
					
						
							|  |  |  | 		!add_string_property(np, "device_type", type) || | 
					
						
							|  |  |  | 		!add_string_property(np, "compatible", compat) || | 
					
						
							|  |  |  | 		!add_raw_property(np, "reg", sizeof(reg), ®) || | 
					
						
							|  |  |  | 		!add_raw_property(np, "linux,unit_address", | 
					
						
							|  |  |  | 			sizeof(unit), &unit)) { | 
					
						
							|  |  |  | 		goto node_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (res) { | 
					
						
							|  |  |  | 		if (!add_raw_property(np, "linux,vio_rsrcname", | 
					
						
							|  |  |  | 				sizeof(res->rsrcname), res->rsrcname) || | 
					
						
							|  |  |  | 			!add_raw_property(np, "linux,vio_type", | 
					
						
							|  |  |  | 				sizeof(res->type), res->type) || | 
					
						
							|  |  |  | 			!add_raw_property(np, "linux,vio_model", | 
					
						
							|  |  |  | 				sizeof(res->model), res->model)) | 
					
						
							|  |  |  | 			goto node_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	np->name = of_get_property(np, "name", NULL); | 
					
						
							|  |  |  | 	np->type = of_get_property(np, "device_type", NULL); | 
					
						
							|  |  |  | 	of_attach_node(np); | 
					
						
							|  |  |  | #ifdef CONFIG_PROC_DEVICETREE
 | 
					
						
							|  |  |  | 	if (parent->pde) { | 
					
						
							|  |  |  | 		struct proc_dir_entry *ent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde); | 
					
						
							|  |  |  | 		if (ent) | 
					
						
							|  |  |  | 			proc_device_tree_add_node(np, ent); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	return np; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  node_free: | 
					
						
							|  |  |  | 	free_node(np); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * This is here so that we can dynamically add viodasd | 
					
						
							|  |  |  |  * devices without exposing all the above infrastructure. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct vio_dev *vio_create_viodasd(u32 unit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *vio_root; | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 	struct vio_dev *vdev = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vio_root = of_find_node_by_path("/vdevice"); | 
					
						
							|  |  |  | 	if (!vio_root) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | 
					
						
							|  |  |  | 			"block", "IBM,iSeries-viodasd", NULL); | 
					
						
							|  |  |  | 	of_node_put(vio_root); | 
					
						
							|  |  |  | 	if (np) { | 
					
						
							|  |  |  | 		vdev = vio_register_device_node(np); | 
					
						
							|  |  |  | 		if (!vdev) | 
					
						
							|  |  |  | 			free_node(np); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return vdev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(vio_create_viodasd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init handle_block_event(struct HvLpEvent *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; | 
					
						
							|  |  |  | 	struct vio_waitevent *pwe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (event == NULL) | 
					
						
							|  |  |  | 		/* Notification that a partition went away! */ | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	/* First, we should NEVER get an int here...only acks */ | 
					
						
							|  |  |  | 	if (hvlpevent_is_int(event)) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "handle_viod_request: " | 
					
						
							|  |  |  | 		       "Yikes! got an int in viodasd event handler!\n"); | 
					
						
							|  |  |  | 		if (hvlpevent_need_ack(event)) { | 
					
						
							|  |  |  | 			event->xRc = HvLpEvent_Rc_InvalidSubtype; | 
					
						
							|  |  |  | 			HvCallEvent_ackLpEvent(event); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | 
					
						
							|  |  |  | 	case vioblockopen: | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Handle a response to an open request.  We get all the | 
					
						
							|  |  |  | 		 * disk information in the response, so update it.  The | 
					
						
							|  |  |  | 		 * correlation token contains a pointer to a waitevent | 
					
						
							|  |  |  | 		 * structure that has a completion in it.  update the | 
					
						
							|  |  |  | 		 * return code in the waitevent structure and post the | 
					
						
							|  |  |  | 		 * completion to wake up the guy who sent the request | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		pwe = (struct vio_waitevent *)event->xCorrelationToken; | 
					
						
							|  |  |  | 		pwe->rc = event->xRc; | 
					
						
							|  |  |  | 		pwe->sub_result = bevent->sub_result; | 
					
						
							|  |  |  | 		complete(&pwe->com); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case vioblockclose: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		printk(KERN_WARNING "handle_viod_request: unexpected subtype!"); | 
					
						
							|  |  |  | 		if (hvlpevent_need_ack(event)) { | 
					
						
							|  |  |  | 			event->xRc = HvLpEvent_Rc_InvalidSubtype; | 
					
						
							|  |  |  | 			HvCallEvent_ackLpEvent(event); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init probe_disk(struct device_node *vio_root, u32 unit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HvLpEvent_Rc hvrc; | 
					
						
							|  |  |  | 	struct vio_waitevent we; | 
					
						
							|  |  |  | 	u16 flags = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | retry: | 
					
						
							|  |  |  | 	init_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Send the open event to OS/400 */ | 
					
						
							|  |  |  | 	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | 
					
						
							|  |  |  | 			HvLpEvent_Type_VirtualIo, | 
					
						
							|  |  |  | 			viomajorsubtype_blockio | vioblockopen, | 
					
						
							|  |  |  | 			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | 
					
						
							|  |  |  | 			viopath_sourceinst(viopath_hostLp), | 
					
						
							|  |  |  | 			viopath_targetinst(viopath_hostLp), | 
					
						
							|  |  |  | 			(u64)(unsigned long)&we, VIOVERSION << 16, | 
					
						
							|  |  |  | 			((u64)unit << 48) | ((u64)flags<< 32), | 
					
						
							|  |  |  | 			0, 0, 0); | 
					
						
							|  |  |  | 	if (hvrc != 0) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n", | 
					
						
							|  |  |  | 			(int)hvrc); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wait_for_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (we.rc != 0) { | 
					
						
							|  |  |  | 		if (flags != 0) | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		/* try again with read only flag set */ | 
					
						
							|  |  |  | 		flags = vioblockflags_ro; | 
					
						
							|  |  |  | 		goto retry; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Send the close event to OS/400.  We DON'T expect a response */ | 
					
						
							|  |  |  | 	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | 
					
						
							|  |  |  | 			HvLpEvent_Type_VirtualIo, | 
					
						
							|  |  |  | 			viomajorsubtype_blockio | vioblockclose, | 
					
						
							|  |  |  | 			HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | 
					
						
							|  |  |  | 			viopath_sourceinst(viopath_hostLp), | 
					
						
							|  |  |  | 			viopath_targetinst(viopath_hostLp), | 
					
						
							|  |  |  | 			0, VIOVERSION << 16, | 
					
						
							|  |  |  | 			((u64)unit << 48) | ((u64)flags << 32), | 
					
						
							|  |  |  | 			0, 0, 0); | 
					
						
							|  |  |  | 	if (hvrc != 0) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "probe_disk: " | 
					
						
							|  |  |  | 		       "bad rc sending event to OS/400 %d\n", (int)hvrc); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, | 
					
						
							|  |  |  | 			"block", "IBM,iSeries-viodasd", NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init get_viodasd_info(struct device_node *vio_root) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 	u32 unit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2); | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "get_viodasd_info: " | 
					
						
							|  |  |  | 		       "error opening path to host partition %d\n", | 
					
						
							|  |  |  | 		       viopath_hostLp); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Initialize our request handler */ | 
					
						
							|  |  |  | 	vio_setHandler(viomajorsubtype_blockio, handle_block_event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++) | 
					
						
							|  |  |  | 		probe_disk(vio_root, unit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vio_clearHandler(viomajorsubtype_blockio); | 
					
						
							|  |  |  | 	viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | static void __init handle_cd_event(struct HvLpEvent *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct viocdlpevent *bevent; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 	struct vio_waitevent *pwe; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!event) | 
					
						
							|  |  |  | 		/* Notification that a partition went away! */ | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* First, we should NEVER get an int here...only acks */ | 
					
						
							|  |  |  | 	if (hvlpevent_is_int(event)) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "handle_cd_event: got an unexpected int\n"); | 
					
						
							|  |  |  | 		if (hvlpevent_need_ack(event)) { | 
					
						
							|  |  |  | 			event->xRc = HvLpEvent_Rc_InvalidSubtype; | 
					
						
							|  |  |  | 			HvCallEvent_ackLpEvent(event); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bevent = (struct viocdlpevent *)event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | 
					
						
							|  |  |  | 	case viocdgetinfo: | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 		pwe = (struct vio_waitevent *)event->xCorrelationToken; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 		pwe->rc = event->xRc; | 
					
						
							|  |  |  | 		pwe->sub_result = bevent->sub_result; | 
					
						
							|  |  |  | 		complete(&pwe->com); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		printk(KERN_WARNING "handle_cd_event: " | 
					
						
							|  |  |  | 			"message with unexpected subtype %0x04X!\n", | 
					
						
							|  |  |  | 			event->xSubtype & VIOMINOR_SUBTYPE_MASK); | 
					
						
							|  |  |  | 		if (hvlpevent_need_ack(event)) { | 
					
						
							|  |  |  | 			event->xRc = HvLpEvent_Rc_InvalidSubtype; | 
					
						
							|  |  |  | 			HvCallEvent_ackLpEvent(event); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init get_viocd_info(struct device_node *vio_root) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HvLpEvent_Rc hvrc; | 
					
						
							|  |  |  | 	u32 unit; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 	struct vio_waitevent we; | 
					
						
							|  |  |  | 	struct vio_resource *unitinfo; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 	dma_addr_t unitinfo_dmaaddr; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING | 
					
						
							|  |  |  | 			"get_viocd_info: error opening path to host partition %d\n", | 
					
						
							|  |  |  | 			viopath_hostLp); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Initialize our request handler */ | 
					
						
							|  |  |  | 	vio_setHandler(viomajorsubtype_cdio, handle_cd_event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	unitinfo = iseries_hv_alloc( | 
					
						
							|  |  |  | 			sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | 
					
						
							|  |  |  | 			&unitinfo_dmaaddr, GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (!unitinfo) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING | 
					
						
							|  |  |  | 			"get_viocd_info: error allocating unitinfo\n"); | 
					
						
							|  |  |  | 		goto clear_handler; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	init_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | 
					
						
							|  |  |  | 			HvLpEvent_Type_VirtualIo, | 
					
						
							|  |  |  | 			viomajorsubtype_cdio | viocdgetinfo, | 
					
						
							|  |  |  | 			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | 
					
						
							|  |  |  | 			viopath_sourceinst(viopath_hostLp), | 
					
						
							|  |  |  | 			viopath_targetinst(viopath_hostLp), | 
					
						
							|  |  |  | 			(u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0, | 
					
						
							|  |  |  | 			sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0); | 
					
						
							|  |  |  | 	if (hvrc != HvLpEvent_Rc_Good) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING | 
					
						
							|  |  |  | 			"get_viocd_info: cdrom error sending event. rc %d\n", | 
					
						
							|  |  |  | 			(int)hvrc); | 
					
						
							|  |  |  | 		goto hv_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wait_for_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (we.rc) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n", | 
					
						
							|  |  |  | 			we.rc, we.sub_result); | 
					
						
							|  |  |  | 		goto hv_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && | 
					
						
							|  |  |  | 			unitinfo[unit].rsrcname[0]; unit++) { | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | 		if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit, | 
					
						
							|  |  |  | 				"block", "IBM,iSeries-viocd", &unitinfo[unit])) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  hv_free: | 
					
						
							|  |  |  | 	iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | 
					
						
							|  |  |  | 			unitinfo, unitinfo_dmaaddr); | 
					
						
							|  |  |  |  clear_handler: | 
					
						
							|  |  |  | 	vio_clearHandler(viomajorsubtype_cdio); | 
					
						
							|  |  |  | 	viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | /* Handle interrupt events for tape */ | 
					
						
							|  |  |  | static void __init handle_tape_event(struct HvLpEvent *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct vio_waitevent *we; | 
					
						
							|  |  |  | 	struct viotapelpevent *tevent = (struct viotapelpevent *)event; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (event == NULL) | 
					
						
							|  |  |  | 		/* Notification that a partition went away! */ | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	we = (struct vio_waitevent *)event->xCorrelationToken; | 
					
						
							|  |  |  | 	switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | 
					
						
							|  |  |  | 	case viotapegetinfo: | 
					
						
							|  |  |  | 		we->rc = tevent->sub_type_result; | 
					
						
							|  |  |  | 		complete(&we->com); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		printk(KERN_WARNING "handle_tape_event: weird ack\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init get_viotape_info(struct device_node *vio_root) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HvLpEvent_Rc hvrc; | 
					
						
							|  |  |  | 	u32 unit; | 
					
						
							|  |  |  | 	struct vio_resource *unitinfo; | 
					
						
							|  |  |  | 	dma_addr_t unitinfo_dmaaddr; | 
					
						
							|  |  |  | 	size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES; | 
					
						
							|  |  |  | 	struct vio_waitevent we; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 21:10:56 +00:00
										 |  |  | 	init_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 	ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "get_viotape_info: " | 
					
						
							|  |  |  | 			"error on viopath_open to hostlp %d\n", ret); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vio_setHandler(viomajorsubtype_tape, handle_tape_event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (!unitinfo) | 
					
						
							|  |  |  | 		goto clear_handler; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(unitinfo, 0, len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | 
					
						
							|  |  |  | 			HvLpEvent_Type_VirtualIo, | 
					
						
							|  |  |  | 			viomajorsubtype_tape | viotapegetinfo, | 
					
						
							|  |  |  | 			HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | 
					
						
							|  |  |  | 			viopath_sourceinst(viopath_hostLp), | 
					
						
							|  |  |  | 			viopath_targetinst(viopath_hostLp), | 
					
						
							|  |  |  | 			(u64)(unsigned long)&we, VIOVERSION << 16, | 
					
						
							|  |  |  | 			unitinfo_dmaaddr, len, 0, 0); | 
					
						
							|  |  |  | 	if (hvrc != HvLpEvent_Rc_Good) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "get_viotape_info: hv error on op %d\n", | 
					
						
							|  |  |  | 				(int)hvrc); | 
					
						
							|  |  |  | 		goto hv_free; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wait_for_completion(&we.com); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) && | 
					
						
							|  |  |  | 			unitinfo[unit].rsrcname[0]; unit++) { | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | 		if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit, | 
					
						
							|  |  |  | 				unit, "byte", "IBM,iSeries-viotape", | 
					
						
							|  |  |  | 				&unitinfo[unit])) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  hv_free: | 
					
						
							|  |  |  | 	iseries_hv_free(len, unitinfo, unitinfo_dmaaddr); | 
					
						
							|  |  |  |  clear_handler: | 
					
						
							|  |  |  | 	vio_clearHandler(viomajorsubtype_tape); | 
					
						
							|  |  |  | 	viopath_close(viopath_hostLp, viomajorsubtype_tape, 2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | static int __init iseries_vio_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *vio_root; | 
					
						
							| 
									
										
										
										
											2007-11-06 17:26:42 +11:00
										 |  |  | 	int ret = -ENODEV; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!firmware_has_feature(FW_FEATURE_ISERIES)) | 
					
						
							| 
									
										
										
										
											2007-11-06 17:26:42 +11:00
										 |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	iommu_vio_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vio_root = of_find_node_by_path("/vdevice"); | 
					
						
							|  |  |  | 	if (!vio_root) | 
					
						
							| 
									
										
										
										
											2007-11-06 17:26:42 +11:00
										 |  |  | 		goto out; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (viopath_hostLp == HvLpIndexInvalid) { | 
					
						
							|  |  |  | 		vio_set_hostlp(); | 
					
						
							|  |  |  | 		/* If we don't have a host, bail out */ | 
					
						
							|  |  |  | 		if (viopath_hostLp == HvLpIndexInvalid) | 
					
						
							|  |  |  | 			goto put_node; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 14:59:54 +10:00
										 |  |  | 	get_viodasd_info(vio_root); | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 	get_viocd_info(vio_root); | 
					
						
							| 
									
										
										
										
											2007-10-11 14:58:31 +10:00
										 |  |  | 	get_viotape_info(vio_root); | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-06 17:26:42 +11:00
										 |  |  | 	ret = 0; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |  put_node: | 
					
						
							|  |  |  | 	of_node_put(vio_root); | 
					
						
							| 
									
										
										
										
											2007-11-06 17:26:42 +11:00
										 |  |  |  out: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2007-10-11 14:57:26 +10:00
										 |  |  | } | 
					
						
							|  |  |  | arch_initcall(iseries_vio_init); |