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 <[EMAIL PROTECTED]>
---

(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/[EMAIL PROTECTED]";
        };
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to