All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
To: <linux-kernel@vger.kernel.org>
Cc: linuxppc-dev@ozlabs.org
Subject: [RFC/PATCH] Block device for the ISS simulator
Date: Fri, 03 Oct 2008 10:08:42 +1000	[thread overview]
Message-ID: <20081003000904.0A812DDEEE@ozlabs.org> (raw)

The ISS simulator is a simple powerpc simulator used among other things
for hardware bringup. It implements a simple memory mapped block device
interface.

This is a simple block driver that attaches to it. Note that the choice
of a major device number is fishy, though because it's a simulator and
not real hardware, it's not necessarily a big deal.

Comments welcome,

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

(And yes, I will try to get ISS to implement an IDE emulation instead
but that's not what's there at this stage)

 arch/powerpc/boot/dts/iss4xx.dts |    5 
 drivers/block/Kconfig            |    4 
 drivers/block/Makefile           |    1 
 drivers/block/iss_blk.c          |  365 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 375 insertions(+)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/drivers/block/iss_blk.c	2008-09-23 11:12:03.000000000 +1000
@@ -0,0 +1,365 @@
+/*
+ * Simple block device for the ISS simulator
+ */
+
+#undef DEBUG
+
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/blkdev.h>
+#include <linux/of.h>
+
+#include <asm/io.h>
+
+#define MAJOR_NR		63	/* FIXME */
+#define NUM_ISS_BLK_MINOR	4
+
+/* Command codes */
+enum {
+	ISS_BD_CMD_NOP		= 0,
+	ISS_BD_CMD_OPEN		= 1,
+	ISS_BD_CMD_CLOSE	= 2,
+	ISS_BD_CMD_READ		= 3,
+	ISS_BD_CMD_WRITE	= 4,
+	ISS_BD_CMD_STATUS	= 5,
+	ISS_BD_CMD_CHKCHANGE	= 6,
+	ISS_BD_CMD_SYNC		= 7,
+	ISS_BD_CMD_GET_BLKSIZE	= 8,
+	ISS_BD_CMD_GET_DEVSIZE	= 9,
+};
+
+/* Status codes */
+enum {
+	ISS_BD_STATUS_OK	= 0,
+	ISS_BD_STATUS_OP_ER	= 1,	/* Open error			       */
+	ISS_BD_ALREADY_OPEN	= 2,	/* Block file already open	       */
+	ISS_BD_NOT_OPEN		= 3,	/* Block file not open		       */
+	ISS_BD_BAD_DEV_NUM	= 4,	/* Bad device number		       */
+	ISS_BD_BAD_SEC_CNT	= 5,	/* Bad sector number		       */
+	ISS_BD_SEEK_ERROR	= 6,	/* Bad sector count		       */
+	ISS_BD_RW_ERROR		= 7,	/* Read/Write error		       */
+	ISS_BD_SIZE_ERROR	= 8,	/* Unable to determine file size       */
+};
+
+struct iss_blk_regs {
+	u8	cmd;
+	u8	pad0[3];
+	u32	stat;
+	u32	sector;
+	u32	count;
+	u32	devno;
+	u32	size;
+	u8	pad1[0x1e8];
+	u8	data[0x800];
+};
+
+struct iss_blk {
+	struct gendisk		*disk;
+	unsigned int		devno;
+	unsigned int		sectsize;
+	unsigned int		capacity;
+	unsigned int		present;
+	unsigned int		changed;
+} iss_blks[NUM_ISS_BLK_MINOR];
+
+static spinlock_t			iss_blk_qlock;
+static spinlock_t			iss_blk_reglock;
+static struct iss_blk_regs __iomem	*iss_blk_regs;
+
+static void iss_blk_setup(struct iss_blk *ib)
+{
+	unsigned long flags;
+	u32 stat;
+
+	pr_debug("iss_blk_setup %d\n", ib->devno);
+
+	spin_lock_irqsave(&iss_blk_reglock, flags);
+	out_8(iss_blk_regs->data, 0);
+	out_be32(&iss_blk_regs->devno, ib->devno);
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_OPEN);
+	stat = in_be32(&iss_blk_regs->stat);
+	if (stat != ISS_BD_STATUS_OK) {
+		pr_debug(" -> no file\n");
+		goto failed;
+	}
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_GET_BLKSIZE);
+	ib->sectsize = in_be32(&iss_blk_regs->size);
+	if (ib->sectsize != 512) {
+		pr_err("issblk: unsupported sector size %d\n", ib->sectsize);
+		goto failed;
+	}
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_GET_DEVSIZE);
+	ib->capacity = in_be32(&iss_blk_regs->size);
+	ib->present = 1;
+	ib->changed = 0;
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+
+	pr_debug(" -> 0x%x sectors 0f %d bytes\n",
+		 ib->capacity, ib->sectsize);
+
+	blk_queue_bounce_limit(ib->disk->queue, BLK_BOUNCE_HIGH);
+	blk_queue_hardsect_size(ib->disk->queue, ib->sectsize);
+	set_capacity(ib->disk, ib->capacity);
+	return;
+
+ failed:
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+}
+
+static int __iss_blk_read(struct iss_blk *ib, void *buffer,
+			  unsigned long sector, unsigned long count)
+{
+	unsigned long lcount, flags;
+	u32 stat;
+
+	pr_debug("__iss_blk_read 0x%ld sectors @ 0x%lx\n", count, sector);
+
+	while(count) {
+		lcount = min(count, 4ul);
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_be32(&iss_blk_regs->sector, sector);
+		out_be32(&iss_blk_regs->count, lcount);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_READ);
+		stat = in_be32(&iss_blk_regs->stat);
+		if (stat != ISS_BD_STATUS_OK) {
+			spin_unlock_irqrestore(&iss_blk_reglock, flags);
+			return -EIO;
+		}
+		memcpy_fromio(buffer, &iss_blk_regs->data, lcount * ib->sectsize);
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+		count -= lcount;
+		sector += lcount;
+		buffer += lcount * ib->sectsize;
+	}
+	return 0;
+}
+
+static int __iss_blk_write(struct iss_blk *ib, void *buffer,
+			   unsigned long sector, unsigned long count)
+{
+	unsigned long lcount, flags;
+	u32 stat;
+
+	pr_debug("__iss_blk_write 0x%ld sectors @ 0x%lx\n", count, sector);
+
+	while(count) {
+		lcount = min(count, 4ul);
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_be32(&iss_blk_regs->sector, sector);
+		out_be32(&iss_blk_regs->count, lcount);
+		memcpy_toio(&iss_blk_regs->data, buffer, lcount * ib->sectsize);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_WRITE);
+		stat = in_be32(&iss_blk_regs->stat);
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+		if (stat != ISS_BD_STATUS_OK)
+			return -EIO;
+		count -= lcount;
+		sector += lcount;
+		buffer += lcount * ib->sectsize;
+	}
+	return 0;
+}
+
+static void iss_blk_do_request(struct request_queue * q)
+{
+	struct iss_blk *ib = q->queuedata;
+	struct request *req;
+	int rc = 0;
+
+	pr_debug("iss_do_request dev %d\n", ib->devno);
+
+	while ((req = elv_next_request(q)) != NULL) {
+		pr_debug(" -> req @ %p, changed: %d\n", req, ib->changed);
+		if (ib->changed) {
+			end_request(req, 0);	/* failure */
+			continue;
+		}
+		switch (rq_data_dir(req)) {
+		case READ:
+			rc = __iss_blk_read(ib, req->buffer, req->sector,
+					    req->current_nr_sectors);
+			break;
+		case WRITE:
+			rc = __iss_blk_write(ib, req->buffer, req->sector,
+					     req->current_nr_sectors);
+		};
+
+		pr_debug(" -> ending request, rc = %d\n", rc);
+		if (rc)
+			end_request(req, 0);	/* failure */
+		else
+			end_request(req, 1);	/* success */
+	}
+}
+
+static int iss_blk_release(struct inode *inode, struct file *file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct iss_blk *ib = disk->private_data;
+	unsigned long flags;
+
+	pr_debug("issblk%d: release !\n", disk->first_minor);
+
+	spin_lock_irqsave(&iss_blk_reglock, flags);
+	out_be32(&iss_blk_regs->devno, ib->devno);
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_SYNC);
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+
+	return 0;
+}
+
+static int iss_blk_revalidate(struct gendisk *disk)
+{
+	struct iss_blk *ib = disk->private_data;
+	unsigned long flags;
+
+	pr_debug("issblk%d: revalidate !\n", disk->first_minor);
+
+	if (ib->present && ib->changed) {
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_CLOSE);
+		ib->present = ib->changed = 0;
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+	}
+	iss_blk_setup(ib);
+	return 0;
+}
+
+static int iss_blk_media_changed(struct gendisk *disk)
+{
+	struct iss_blk *ib = disk->private_data;
+
+	pr_debug("issblk%d: media_changed !\n", disk->first_minor);
+
+	/* Not implemented -> query change status from ISS */
+
+	return ib->changed;
+}
+
+static int iss_blk_open(struct inode *inode, struct file *file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct iss_blk *ib = disk->private_data;
+
+	pr_debug("issblk%d: open !\n", disk->first_minor);
+
+	check_disk_change(inode->i_bdev);
+	if (ib->changed)
+		iss_blk_setup(ib);
+	if (!ib->present)
+		return -ENOMEDIUM;
+	return 0;
+}
+
+static struct block_device_operations iss_blk_fops = {
+      .owner		= THIS_MODULE,
+      .open		= iss_blk_open,
+      .release		= iss_blk_release,
+      .media_changed	= iss_blk_media_changed,
+      .revalidate_disk	= iss_blk_revalidate,
+};
+
+static int __init iss_blk_init(void)
+{
+	struct device_node *np;
+	int i;
+
+	pr_debug("iss_regs offsets:\n");
+	pr_debug("  cmd    : 0x%x\n", offsetof(struct iss_blk_regs, cmd));
+	pr_debug("  stat   : 0x%x\n", offsetof(struct iss_blk_regs, stat));
+	pr_debug("  sector : 0x%x\n", offsetof(struct iss_blk_regs, sector));
+	pr_debug("  count  : 0x%x\n", offsetof(struct iss_blk_regs, count));
+	pr_debug("  devno  : 0x%x\n", offsetof(struct iss_blk_regs, devno));
+	pr_debug("  size   : 0x%x\n", offsetof(struct iss_blk_regs, size));
+	pr_debug("  data   : 0x%x\n", offsetof(struct iss_blk_regs, data));
+
+	np = of_find_node_by_path("/iss-block");
+	if (np == NULL)
+		return -ENODEV;
+	iss_blk_regs = of_iomap(np, 0);
+	if (iss_blk_regs == NULL) {
+		pr_err("issblk: Failed to map registers\n");
+		return -ENOMEM;
+	}
+
+	if (register_blkdev(MAJOR_NR, "iss_blk"))
+		return -EIO;
+
+	spin_lock_init(&iss_blk_qlock);
+	spin_lock_init(&iss_blk_reglock);
+
+	printk(KERN_INFO "ISS Block driver initializing for %d minors\n",
+	       NUM_ISS_BLK_MINOR);
+
+	for (i = 0; i < NUM_ISS_BLK_MINOR; i++) {
+		struct gendisk *disk = alloc_disk(1);
+		struct request_queue *q;
+		struct iss_blk *ib = &iss_blks[i];
+
+		if (!disk) {
+			pr_err("issblk%d: Failed to allocate disk\n", i);
+			break;
+		}
+
+		q = blk_init_queue(iss_blk_do_request, &iss_blk_qlock);
+		if (q == NULL) {
+			pr_err("issblk%d: Failed to init queue\n", i);
+			put_disk(disk);
+			break;
+		}
+		q->queuedata = ib;
+
+		ib->disk = disk;
+		ib->devno = i;
+		ib->present = 0;
+		ib->changed = 0;
+		ib->capacity = 0;
+		ib->sectsize = 512;
+
+		disk->major = MAJOR_NR;
+		disk->first_minor = i;
+		disk->fops = &iss_blk_fops;
+		disk->private_data = &iss_blks[i];
+		disk->flags = GENHD_FL_REMOVABLE;
+		disk->queue = q;
+		sprintf(disk->disk_name, "issblk%d", i);
+
+		iss_blk_setup(ib);
+
+		add_disk(disk);
+	}
+
+	return 0;
+}
+
+static void __exit iss_blk_exit(void)
+{
+	int i;
+
+	unregister_blkdev(MAJOR_NR, "iss_blk");
+
+	for (i = 0; i < NUM_ISS_BLK_MINOR; i++) {
+		struct iss_blk *ib = &iss_blks[i];
+
+		if (ib->present) {
+			out_be32(&iss_blk_regs->devno, ib->devno);
+			out_8(&iss_blk_regs->cmd, ISS_BD_CMD_CLOSE);
+		}
+	}
+}
+
+module_init(iss_blk_init);
+module_exit(iss_blk_exit);
+
+MODULE_DESCRIPTION("ISS Simulator Block Device");
+MODULE_LICENSE("GPL");
Index: linux-work/drivers/block/Kconfig
===================================================================
--- linux-work.orig/drivers/block/Kconfig	2008-07-17 14:43:58.000000000 +1000
+++ linux-work/drivers/block/Kconfig	2008-09-23 11:12:03.000000000 +1000
@@ -357,6 +357,10 @@ config BLK_DEV_XIP
 	  will prevent RAM block device backing store memory from being
 	  allocated from highmem (only a problem for highmem systems).
 
+config BLK_DEV_ISS
+       bool "Support ISS Simulator Block Device"
+       default n
+
 config CDROM_PKTCDVD
 	tristate "Packet writing on CD/DVD media"
 	depends on !UML
Index: linux-work/drivers/block/Makefile
===================================================================
--- linux-work.orig/drivers/block/Makefile	2008-07-17 14:43:58.000000000 +1000
+++ linux-work/drivers/block/Makefile	2008-09-23 11:12:03.000000000 +1000
@@ -30,5 +30,6 @@ obj-$(CONFIG_VIODASD)		+= viodasd.o
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_UB)	+= ub.o
 obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
+obj-$(CONFIG_BLK_DEV_ISS)	+= iss_blk.o
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
Index: linux-work/arch/powerpc/boot/dts/iss4xx.dts
===================================================================
--- linux-work.orig/arch/powerpc/boot/dts/iss4xx.dts	2008-09-23 11:12:02.000000000 +1000
+++ linux-work/arch/powerpc/boot/dts/iss4xx.dts	2008-09-23 11:12:03.000000000 +1000
@@ -101,6 +101,11 @@
 		};
 	};
 
+	iss-block {
+		compatible = "ibm,iss-sim-block-device";
+		reg = <0 0xEF701000 0x1000>;
+	};
+
 	chosen {
 		linux,stdout-path = "/plb/opb/serial@40000200";
 	};

WARNING: multiple messages have this Message-ID (diff)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
To: <linux-kernel@vger.kernel.org>
Cc: <linuxppc-dev@ozlabs.org>, Josh Boyer <jwboyer@linux.vnet.ibm.com>
Subject: [RFC/PATCH] Block device for the ISS simulator
Date: Fri, 03 Oct 2008 10:08:42 +1000	[thread overview]
Message-ID: <20081003000904.0A812DDEEE@ozlabs.org> (raw)

The ISS simulator is a simple powerpc simulator used among other things
for hardware bringup. It implements a simple memory mapped block device
interface.

This is a simple block driver that attaches to it. Note that the choice
of a major device number is fishy, though because it's a simulator and
not real hardware, it's not necessarily a big deal.

Comments welcome,

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

(And yes, I will try to get ISS to implement an IDE emulation instead
but that's not what's there at this stage)

 arch/powerpc/boot/dts/iss4xx.dts |    5 
 drivers/block/Kconfig            |    4 
 drivers/block/Makefile           |    1 
 drivers/block/iss_blk.c          |  365 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 375 insertions(+)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/drivers/block/iss_blk.c	2008-09-23 11:12:03.000000000 +1000
@@ -0,0 +1,365 @@
+/*
+ * Simple block device for the ISS simulator
+ */
+
+#undef DEBUG
+
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/blkdev.h>
+#include <linux/of.h>
+
+#include <asm/io.h>
+
+#define MAJOR_NR		63	/* FIXME */
+#define NUM_ISS_BLK_MINOR	4
+
+/* Command codes */
+enum {
+	ISS_BD_CMD_NOP		= 0,
+	ISS_BD_CMD_OPEN		= 1,
+	ISS_BD_CMD_CLOSE	= 2,
+	ISS_BD_CMD_READ		= 3,
+	ISS_BD_CMD_WRITE	= 4,
+	ISS_BD_CMD_STATUS	= 5,
+	ISS_BD_CMD_CHKCHANGE	= 6,
+	ISS_BD_CMD_SYNC		= 7,
+	ISS_BD_CMD_GET_BLKSIZE	= 8,
+	ISS_BD_CMD_GET_DEVSIZE	= 9,
+};
+
+/* Status codes */
+enum {
+	ISS_BD_STATUS_OK	= 0,
+	ISS_BD_STATUS_OP_ER	= 1,	/* Open error			       */
+	ISS_BD_ALREADY_OPEN	= 2,	/* Block file already open	       */
+	ISS_BD_NOT_OPEN		= 3,	/* Block file not open		       */
+	ISS_BD_BAD_DEV_NUM	= 4,	/* Bad device number		       */
+	ISS_BD_BAD_SEC_CNT	= 5,	/* Bad sector number		       */
+	ISS_BD_SEEK_ERROR	= 6,	/* Bad sector count		       */
+	ISS_BD_RW_ERROR		= 7,	/* Read/Write error		       */
+	ISS_BD_SIZE_ERROR	= 8,	/* Unable to determine file size       */
+};
+
+struct iss_blk_regs {
+	u8	cmd;
+	u8	pad0[3];
+	u32	stat;
+	u32	sector;
+	u32	count;
+	u32	devno;
+	u32	size;
+	u8	pad1[0x1e8];
+	u8	data[0x800];
+};
+
+struct iss_blk {
+	struct gendisk		*disk;
+	unsigned int		devno;
+	unsigned int		sectsize;
+	unsigned int		capacity;
+	unsigned int		present;
+	unsigned int		changed;
+} iss_blks[NUM_ISS_BLK_MINOR];
+
+static spinlock_t			iss_blk_qlock;
+static spinlock_t			iss_blk_reglock;
+static struct iss_blk_regs __iomem	*iss_blk_regs;
+
+static void iss_blk_setup(struct iss_blk *ib)
+{
+	unsigned long flags;
+	u32 stat;
+
+	pr_debug("iss_blk_setup %d\n", ib->devno);
+
+	spin_lock_irqsave(&iss_blk_reglock, flags);
+	out_8(iss_blk_regs->data, 0);
+	out_be32(&iss_blk_regs->devno, ib->devno);
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_OPEN);
+	stat = in_be32(&iss_blk_regs->stat);
+	if (stat != ISS_BD_STATUS_OK) {
+		pr_debug(" -> no file\n");
+		goto failed;
+	}
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_GET_BLKSIZE);
+	ib->sectsize = in_be32(&iss_blk_regs->size);
+	if (ib->sectsize != 512) {
+		pr_err("issblk: unsupported sector size %d\n", ib->sectsize);
+		goto failed;
+	}
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_GET_DEVSIZE);
+	ib->capacity = in_be32(&iss_blk_regs->size);
+	ib->present = 1;
+	ib->changed = 0;
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+
+	pr_debug(" -> 0x%x sectors 0f %d bytes\n",
+		 ib->capacity, ib->sectsize);
+
+	blk_queue_bounce_limit(ib->disk->queue, BLK_BOUNCE_HIGH);
+	blk_queue_hardsect_size(ib->disk->queue, ib->sectsize);
+	set_capacity(ib->disk, ib->capacity);
+	return;
+
+ failed:
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+}
+
+static int __iss_blk_read(struct iss_blk *ib, void *buffer,
+			  unsigned long sector, unsigned long count)
+{
+	unsigned long lcount, flags;
+	u32 stat;
+
+	pr_debug("__iss_blk_read 0x%ld sectors @ 0x%lx\n", count, sector);
+
+	while(count) {
+		lcount = min(count, 4ul);
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_be32(&iss_blk_regs->sector, sector);
+		out_be32(&iss_blk_regs->count, lcount);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_READ);
+		stat = in_be32(&iss_blk_regs->stat);
+		if (stat != ISS_BD_STATUS_OK) {
+			spin_unlock_irqrestore(&iss_blk_reglock, flags);
+			return -EIO;
+		}
+		memcpy_fromio(buffer, &iss_blk_regs->data, lcount * ib->sectsize);
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+		count -= lcount;
+		sector += lcount;
+		buffer += lcount * ib->sectsize;
+	}
+	return 0;
+}
+
+static int __iss_blk_write(struct iss_blk *ib, void *buffer,
+			   unsigned long sector, unsigned long count)
+{
+	unsigned long lcount, flags;
+	u32 stat;
+
+	pr_debug("__iss_blk_write 0x%ld sectors @ 0x%lx\n", count, sector);
+
+	while(count) {
+		lcount = min(count, 4ul);
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_be32(&iss_blk_regs->sector, sector);
+		out_be32(&iss_blk_regs->count, lcount);
+		memcpy_toio(&iss_blk_regs->data, buffer, lcount * ib->sectsize);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_WRITE);
+		stat = in_be32(&iss_blk_regs->stat);
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+		if (stat != ISS_BD_STATUS_OK)
+			return -EIO;
+		count -= lcount;
+		sector += lcount;
+		buffer += lcount * ib->sectsize;
+	}
+	return 0;
+}
+
+static void iss_blk_do_request(struct request_queue * q)
+{
+	struct iss_blk *ib = q->queuedata;
+	struct request *req;
+	int rc = 0;
+
+	pr_debug("iss_do_request dev %d\n", ib->devno);
+
+	while ((req = elv_next_request(q)) != NULL) {
+		pr_debug(" -> req @ %p, changed: %d\n", req, ib->changed);
+		if (ib->changed) {
+			end_request(req, 0);	/* failure */
+			continue;
+		}
+		switch (rq_data_dir(req)) {
+		case READ:
+			rc = __iss_blk_read(ib, req->buffer, req->sector,
+					    req->current_nr_sectors);
+			break;
+		case WRITE:
+			rc = __iss_blk_write(ib, req->buffer, req->sector,
+					     req->current_nr_sectors);
+		};
+
+		pr_debug(" -> ending request, rc = %d\n", rc);
+		if (rc)
+			end_request(req, 0);	/* failure */
+		else
+			end_request(req, 1);	/* success */
+	}
+}
+
+static int iss_blk_release(struct inode *inode, struct file *file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct iss_blk *ib = disk->private_data;
+	unsigned long flags;
+
+	pr_debug("issblk%d: release !\n", disk->first_minor);
+
+	spin_lock_irqsave(&iss_blk_reglock, flags);
+	out_be32(&iss_blk_regs->devno, ib->devno);
+	out_8(&iss_blk_regs->cmd, ISS_BD_CMD_SYNC);
+	spin_unlock_irqrestore(&iss_blk_reglock, flags);
+
+	return 0;
+}
+
+static int iss_blk_revalidate(struct gendisk *disk)
+{
+	struct iss_blk *ib = disk->private_data;
+	unsigned long flags;
+
+	pr_debug("issblk%d: revalidate !\n", disk->first_minor);
+
+	if (ib->present && ib->changed) {
+		spin_lock_irqsave(&iss_blk_reglock, flags);
+		out_be32(&iss_blk_regs->devno, ib->devno);
+		out_8(&iss_blk_regs->cmd, ISS_BD_CMD_CLOSE);
+		ib->present = ib->changed = 0;
+		spin_unlock_irqrestore(&iss_blk_reglock, flags);
+	}
+	iss_blk_setup(ib);
+	return 0;
+}
+
+static int iss_blk_media_changed(struct gendisk *disk)
+{
+	struct iss_blk *ib = disk->private_data;
+
+	pr_debug("issblk%d: media_changed !\n", disk->first_minor);
+
+	/* Not implemented -> query change status from ISS */
+
+	return ib->changed;
+}
+
+static int iss_blk_open(struct inode *inode, struct file *file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct iss_blk *ib = disk->private_data;
+
+	pr_debug("issblk%d: open !\n", disk->first_minor);
+
+	check_disk_change(inode->i_bdev);
+	if (ib->changed)
+		iss_blk_setup(ib);
+	if (!ib->present)
+		return -ENOMEDIUM;
+	return 0;
+}
+
+static struct block_device_operations iss_blk_fops = {
+      .owner		= THIS_MODULE,
+      .open		= iss_blk_open,
+      .release		= iss_blk_release,
+      .media_changed	= iss_blk_media_changed,
+      .revalidate_disk	= iss_blk_revalidate,
+};
+
+static int __init iss_blk_init(void)
+{
+	struct device_node *np;
+	int i;
+
+	pr_debug("iss_regs offsets:\n");
+	pr_debug("  cmd    : 0x%x\n", offsetof(struct iss_blk_regs, cmd));
+	pr_debug("  stat   : 0x%x\n", offsetof(struct iss_blk_regs, stat));
+	pr_debug("  sector : 0x%x\n", offsetof(struct iss_blk_regs, sector));
+	pr_debug("  count  : 0x%x\n", offsetof(struct iss_blk_regs, count));
+	pr_debug("  devno  : 0x%x\n", offsetof(struct iss_blk_regs, devno));
+	pr_debug("  size   : 0x%x\n", offsetof(struct iss_blk_regs, size));
+	pr_debug("  data   : 0x%x\n", offsetof(struct iss_blk_regs, data));
+
+	np = of_find_node_by_path("/iss-block");
+	if (np == NULL)
+		return -ENODEV;
+	iss_blk_regs = of_iomap(np, 0);
+	if (iss_blk_regs == NULL) {
+		pr_err("issblk: Failed to map registers\n");
+		return -ENOMEM;
+	}
+
+	if (register_blkdev(MAJOR_NR, "iss_blk"))
+		return -EIO;
+
+	spin_lock_init(&iss_blk_qlock);
+	spin_lock_init(&iss_blk_reglock);
+
+	printk(KERN_INFO "ISS Block driver initializing for %d minors\n",
+	       NUM_ISS_BLK_MINOR);
+
+	for (i = 0; i < NUM_ISS_BLK_MINOR; i++) {
+		struct gendisk *disk = alloc_disk(1);
+		struct request_queue *q;
+		struct iss_blk *ib = &iss_blks[i];
+
+		if (!disk) {
+			pr_err("issblk%d: Failed to allocate disk\n", i);
+			break;
+		}
+
+		q = blk_init_queue(iss_blk_do_request, &iss_blk_qlock);
+		if (q == NULL) {
+			pr_err("issblk%d: Failed to init queue\n", i);
+			put_disk(disk);
+			break;
+		}
+		q->queuedata = ib;
+
+		ib->disk = disk;
+		ib->devno = i;
+		ib->present = 0;
+		ib->changed = 0;
+		ib->capacity = 0;
+		ib->sectsize = 512;
+
+		disk->major = MAJOR_NR;
+		disk->first_minor = i;
+		disk->fops = &iss_blk_fops;
+		disk->private_data = &iss_blks[i];
+		disk->flags = GENHD_FL_REMOVABLE;
+		disk->queue = q;
+		sprintf(disk->disk_name, "issblk%d", i);
+
+		iss_blk_setup(ib);
+
+		add_disk(disk);
+	}
+
+	return 0;
+}
+
+static void __exit iss_blk_exit(void)
+{
+	int i;
+
+	unregister_blkdev(MAJOR_NR, "iss_blk");
+
+	for (i = 0; i < NUM_ISS_BLK_MINOR; i++) {
+		struct iss_blk *ib = &iss_blks[i];
+
+		if (ib->present) {
+			out_be32(&iss_blk_regs->devno, ib->devno);
+			out_8(&iss_blk_regs->cmd, ISS_BD_CMD_CLOSE);
+		}
+	}
+}
+
+module_init(iss_blk_init);
+module_exit(iss_blk_exit);
+
+MODULE_DESCRIPTION("ISS Simulator Block Device");
+MODULE_LICENSE("GPL");
Index: linux-work/drivers/block/Kconfig
===================================================================
--- linux-work.orig/drivers/block/Kconfig	2008-07-17 14:43:58.000000000 +1000
+++ linux-work/drivers/block/Kconfig	2008-09-23 11:12:03.000000000 +1000
@@ -357,6 +357,10 @@ config BLK_DEV_XIP
 	  will prevent RAM block device backing store memory from being
 	  allocated from highmem (only a problem for highmem systems).
 
+config BLK_DEV_ISS
+       bool "Support ISS Simulator Block Device"
+       default n
+
 config CDROM_PKTCDVD
 	tristate "Packet writing on CD/DVD media"
 	depends on !UML
Index: linux-work/drivers/block/Makefile
===================================================================
--- linux-work.orig/drivers/block/Makefile	2008-07-17 14:43:58.000000000 +1000
+++ linux-work/drivers/block/Makefile	2008-09-23 11:12:03.000000000 +1000
@@ -30,5 +30,6 @@ obj-$(CONFIG_VIODASD)		+= viodasd.o
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_UB)	+= ub.o
 obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
+obj-$(CONFIG_BLK_DEV_ISS)	+= iss_blk.o
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
Index: linux-work/arch/powerpc/boot/dts/iss4xx.dts
===================================================================
--- linux-work.orig/arch/powerpc/boot/dts/iss4xx.dts	2008-09-23 11:12:02.000000000 +1000
+++ linux-work/arch/powerpc/boot/dts/iss4xx.dts	2008-09-23 11:12:03.000000000 +1000
@@ -101,6 +101,11 @@
 		};
 	};
 
+	iss-block {
+		compatible = "ibm,iss-sim-block-device";
+		reg = <0 0xEF701000 0x1000>;
+	};
+
 	chosen {
 		linux,stdout-path = "/plb/opb/serial@40000200";
 	};

             reply	other threads:[~2008-10-03  0:08 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-10-03  0:08 Benjamin Herrenschmidt [this message]
2008-10-03  0:08 ` [RFC/PATCH] Block device for the ISS simulator Benjamin Herrenschmidt
2008-10-03  4:44 ` Andi Kleen
2008-10-03  4:44   ` Andi Kleen
2008-10-03  8:35 ` Christoph Hellwig
2008-10-03  8:35   ` Christoph Hellwig
2008-10-03  9:29   ` Benjamin Herrenschmidt
2008-10-03  9:29     ` Benjamin Herrenschmidt
2008-10-03 12:03 ` Josh Boyer
2008-10-03 12:03   ` Josh Boyer
2008-10-03 12:21   ` Benjamin Herrenschmidt
2008-10-03 12:21     ` Benjamin Herrenschmidt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20081003000904.0A812DDEEE@ozlabs.org \
    --to=benh@kernel.crashing.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.