Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b833b481 authored by Stephen Rothwell's avatar Stephen Rothwell Committed by Paul Mackerras
Browse files

[POWERPC] iSeries: Move detection of virtual cdroms



Now we will only have entries in the device tree for the actual existing
devices (including their OS/400 properties).  This way viocd.c gets all
the information about the devices from the device tree.

Signed-off-by: default avatarStephen Rothwell <sfr@canb.auug.org.au>
Acked-by: default avatarJens Axboe <jens.axboe@oracle.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent dd9b67ab
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -247,9 +247,6 @@ static int __init vio_bus_init(void)
	int err;
	struct device_node *node_vroot;

	if (firmware_has_feature(FW_FEATURE_ISERIES))
		iommu_vio_init();

	err = bus_register(&vio_bus_type);
	if (err) {
		printk(KERN_ERR "failed to register VIO bus\n");
+1 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt_mod.o mf.o lpevents.o \
	hvcall.o proc.o htab.o iommu.o misc.o irq.o
obj-$(CONFIG_PCI) += pci.o vpdinfo.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_VIOPATH) += viopath.o
obj-$(CONFIG_VIOPATH) += viopath.o vio.o
obj-$(CONFIG_MODULES) += ksyms.o

quiet_cmd_dt_strings = DT_STR  $@
+0 −4
Original line number Diff line number Diff line
@@ -381,10 +381,6 @@ static void __init dt_vdevices(struct iseries_flat_dt *dt)
		dt_do_vdevice(dt, "viodasd", reg, i, device_type_block,
				"IBM,iSeries-viodasd", 1);
	reg += HVMAXARCHITECTEDVIRTUALDISKS;

	for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++)
		dt_do_vdevice(dt, "viocd", reg, i, device_type_block,
				"IBM,iSeries-viocd", 1);
	reg += HVMAXARCHITECTEDVIRTUALCDROMS;

	for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++)
+317 −0
Original line number Diff line number Diff line
/*
 * Legacy iSeries specific vio initialisation
 * that needs to be built in (not a module).
 *
 * © Copyright 2007 IBM Corporation
 *	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>

#include <asm/firmware.h>
#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

static struct property * __init new_property(const char *name, int length,
		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;
}

static void __init free_property(struct property *np)
{
	kfree(np);
}

static struct device_node * __init new_node(const char *path,
		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;
}

static void __init free_node(struct device_node *np)
{
	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);
}

static int __init add_string_property(struct device_node *np, const char *name,
		const char *value)
{
	struct property *nprop = new_property(name, strlen(value) + 1, value);

	if (!nprop)
		return 0;
	prom_add_property(np, nprop);
	return 1;
}

static int __init add_raw_property(struct device_node *np, const char *name,
		int length, const void *value)
{
	struct property *nprop = new_property(name, length, value);

	if (!nprop)
		return 0;
	prom_add_property(np, nprop);
	return 1;
}

struct viocd_waitevent {
	struct completion	com;
	int			rc;
	u16			sub_result;
};

struct cdrom_info {
	char	rsrcname[10];
	char	type[4];
	char	model[3];
};

static void __init handle_cd_event(struct HvLpEvent *event)
{
	struct viocdlpevent *bevent;
	struct viocd_waitevent *pwe;

	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:
		pwe = (struct viocd_waitevent *)event->xCorrelationToken;
		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;
	struct viocd_waitevent we;
	struct cdrom_info *unitinfo;
	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++) {
		struct device_node *np;
		char name[64];
		u32 reg = FIRST_VIOCD + unit;

		snprintf(name, sizeof(name), "/vdevice/viocd@%08x", reg);
		np = new_node(name, vio_root);
		if (!np)
			goto hv_free;
		if (!add_string_property(np, "name", "viocd") ||
			!add_string_property(np, "device_type", "block") ||
			!add_string_property(np, "compatible",
				"IBM,iSeries-viocd") ||
			!add_raw_property(np, "reg", sizeof(reg), &reg) ||
			!add_raw_property(np, "linux,unit_address",
				sizeof(unit), &unit) ||
			!add_raw_property(np, "linux,vio_rsrcname",
				sizeof(unitinfo[unit].rsrcname),
				unitinfo[unit].rsrcname) ||
			!add_raw_property(np, "linux,vio_type",
				sizeof(unitinfo[unit].type),
				unitinfo[unit].type) ||
			!add_raw_property(np, "linux,vio_model",
				sizeof(unitinfo[unit].model),
				unitinfo[unit].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 (vio_root->pde) {
			struct proc_dir_entry *ent;

			ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
					vio_root->pde);
			if (ent)
				proc_device_tree_add_node(np, ent);
		}
#endif
		continue;

 node_free:
		free_node(np);
		break;
	}

 hv_free:
	iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
			unitinfo, unitinfo_dmaaddr);
 clear_handler:
	vio_clearHandler(viomajorsubtype_cdio);
	viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
}

static int __init iseries_vio_init(void)
{
	struct device_node *vio_root;

	if (!firmware_has_feature(FW_FEATURE_ISERIES))
		return -ENODEV;

	iommu_vio_init();

	vio_root = of_find_node_by_path("/vdevice");
	if (!vio_root)
		return -ENODEV;

	if (viopath_hostLp == HvLpIndexInvalid) {
		vio_set_hostlp();
		/* If we don't have a host, bail out */
		if (viopath_hostLp == HvLpIndexInvalid)
			goto put_node;
	}

	get_viocd_info(vio_root);

	return 0;

 put_node:
	of_node_put(vio_root);
	return -ENODEV;
}
arch_initcall(iseries_vio_init);
+19 −97
Original line number Diff line number Diff line
@@ -56,30 +56,6 @@
#define VIOCD_KERN_WARNING		KERN_WARNING "viocd: "
#define VIOCD_KERN_INFO			KERN_INFO "viocd: "

struct viocdlpevent {
	struct HvLpEvent	event;
	u32			reserved;
	u16			version;
	u16			sub_result;
	u16			disk;
	u16			flags;
	u32			token;
	u64			offset;		/* On open, max number of disks */
	u64			len;		/* On open, size of the disk */
	u32			block_size;	/* Only set on open */
	u32			media_size;	/* Only set on open */
};

enum viocdsubtype {
	viocdopen = 0x0001,
	viocdclose = 0x0002,
	viocdread = 0x0003,
	viocdwrite = 0x0004,
	viocdlockdoor = 0x0005,
	viocdgetinfo = 0x0006,
	viocdcheck = 0x0007
};

/*
 * Should probably make this a module parameter....sigh
 */
@@ -131,17 +107,13 @@ static struct capability_entry capability_table[] __initdata = {
/* These are our internal structures for keeping track of devices */
static int viocd_numdev;

struct cdrom_info {
	char	rsrcname[10];
	char	type[4];
	char	model[3];
};

struct disk_info {
	struct gendisk			*viocd_disk;
	struct cdrom_device_info	viocd_info;
	struct device			*dev;
	struct cdrom_info		unitinfo;
	const char			*rsrcname;
	const char			*type;
	const char			*model;
};
static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];

@@ -159,9 +131,9 @@ static int proc_viocd_show(struct seq_file *m, void *v)
	for (i = 0; i < viocd_numdev; i++) {
		seq_printf(m, "viocd device %d is iSeries resource %10.10s"
				"type %4.4s, model %3.3s\n",
				i, viocd_diskinfo[i].unitinfo.rsrcname,
				viocd_diskinfo[i].unitinfo.type,
				viocd_diskinfo[i].unitinfo.model);
				i, viocd_diskinfo[i].rsrcname,
				viocd_diskinfo[i].type,
				viocd_diskinfo[i].model);
	}
	return 0;
}
@@ -211,61 +183,6 @@ struct block_device_operations viocd_fops = {
	.media_changed =	viocd_blk_media_changed,
};

/* Get info on CD devices from OS/400 */
static void __init get_viocd_info(void)
{
	HvLpEvent_Rc hvrc;
	int i;
	struct viocd_waitevent we;
	struct cdrom_info *viocd_unitinfo;
	dma_addr_t unitinfo_dmaaddr;

	viocd_unitinfo = iseries_hv_alloc(
			sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
			&unitinfo_dmaaddr, GFP_ATOMIC);
	if (viocd_unitinfo == NULL) {
		printk(VIOCD_KERN_WARNING "error allocating unitinfo\n");
		return;
	}

	memset(viocd_unitinfo, 0, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD);

	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(*viocd_unitinfo) * VIOCD_MAX_CD, 0);
	if (hvrc != HvLpEvent_Rc_Good) {
		printk(VIOCD_KERN_WARNING "cdrom error sending event. rc %d\n",
				(int)hvrc);
		goto error_ret;
	}

	wait_for_completion(&we.com);

	if (we.rc) {
		const struct vio_error_entry *err =
			vio_lookup_rc(viocd_err_table, we.sub_result);
		printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on getinfo: %s\n",
				we.rc, we.sub_result, err->msg);
		goto error_ret;
	}

	for (i = 0; (i < VIOCD_MAX_CD) && viocd_unitinfo[i].rsrcname[0]; i++) {
		viocd_diskinfo[viocd_numdev].unitinfo = viocd_unitinfo[i];
		viocd_numdev++;
	}

error_ret:
	iseries_hv_free(sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
			viocd_unitinfo, unitinfo_dmaaddr);
}

static int viocd_open(struct cdrom_device_info *cdi, int purpose)
{
        struct disk_info *diskinfo = cdi->handle;
@@ -576,7 +493,6 @@ static void vio_handle_cd_event(struct HvLpEvent *event)
					bevent->block_size / 512);
		}
		/* FALLTHROUGH !! */
	case viocdgetinfo:
	case viocdlockdoor:
		pwe = (struct viocd_waitevent *)event->xCorrelationToken;
return_complete:
@@ -660,22 +576,30 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
	int deviceno;
	struct disk_info *d;
	struct cdrom_device_info *c;
	struct cdrom_info *ci;
	struct request_queue *q;
	struct device_node *node = vdev->dev.archdata.of_node;

	deviceno = vdev->unit_address;
	if (deviceno >= viocd_numdev)
	if (deviceno > VIOCD_MAX_CD)
		return -ENODEV;
	if (!node)
		return -ENODEV;

	if (deviceno >= viocd_numdev)
		viocd_numdev = deviceno + 1;

	d = &viocd_diskinfo[deviceno];
	d->rsrcname = of_get_property(node, "linux,vio_rsrcname", NULL);
	d->type = of_get_property(node, "linux,vio_type", NULL);
	d->model = of_get_property(node, "linux,vio_model", NULL);

	c = &d->viocd_info;
	ci = &d->unitinfo;

	c->ops = &viocd_dops;
	c->speed = 4;
	c->capacity = 1;
	c->handle = d;
	c->mask = ~find_capability(ci->type);
	c->mask = ~find_capability(d->type);
	sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);

	if (register_cdrom(c) != 0) {
@@ -685,7 +609,7 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
	}
	printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
			"type %4.4s, model %3.3s\n",
			c->name, ci->rsrcname, ci->type, ci->model);
			c->name, d->rsrcname, d->type, d->model);
	q = blk_init_queue(do_viocd_request, &viocd_reqlock);
	if (q == NULL) {
		printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n",
@@ -794,8 +718,6 @@ static int __init viocd_init(void)
	/* Initialize our request handler */
	vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event);

	get_viocd_info();

	spin_lock_init(&viocd_reqlock);

	ret = vio_register_driver(&viocd_driver);
Loading