linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2.6.20 0/2] imu-scsi for iop13xx
@ 2007-02-13 16:45 Dan Williams
  2007-02-13 16:47 ` [PATCH 2.6.20 1/2] iop13xx: add base support for the imu Dan Williams
  2007-02-13 16:47 ` [PATCH 2.6.20 2/2] iop13xx: imu scsi driver Dan Williams
  0 siblings, 2 replies; 3+ messages in thread
From: Dan Williams @ 2007-02-13 16:45 UTC (permalink / raw)
  To: linux, James.Bottomley; +Cc: greg.b.tucker, linux-scsi, linux-arm.kernel

Here is the iop13xx imu-scsi driver with the recommended scsi changes and some additional cleanups.  I will submit both patches to Russell's tracker once the scsi bits achieve your sign-off James.

Any objections, comments?

Dan

[ note: linux-arm-kernel@lists.arm.linux.org.uk is subscriber only ]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 2.6.20 1/2] iop13xx: add base support for the imu
  2007-02-13 16:45 [PATCH 2.6.20 0/2] imu-scsi for iop13xx Dan Williams
@ 2007-02-13 16:47 ` Dan Williams
  2007-02-13 16:47 ` [PATCH 2.6.20 2/2] iop13xx: imu scsi driver Dan Williams
  1 sibling, 0 replies; 3+ messages in thread
From: Dan Williams @ 2007-02-13 16:47 UTC (permalink / raw)
  To: linux, James.Bottomley; +Cc: greg.b.tucker, linux-scsi, linux-arm.kernel

From: Greg Tucker <greg.b.tucker@intel.com>

The interprocessor messaging unit supports mailbox style communication
between the two Xscale cores on iop342.
    
Changelog:
* cleaned up static functions and exports

Signed-off-by: Greg Tucker <greg.b.tucker@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---

 arch/arm/mach-iop13xx/Kconfig      |    2 
 arch/arm/mach-iop13xx/Makefile     |    1 
 arch/arm/mach-iop13xx/imu/Kconfig  |   19 ++
 arch/arm/mach-iop13xx/imu/Makefile |    3 
 arch/arm/mach-iop13xx/imu/common.c |  294 ++++++++++++++++++++++++
 arch/arm/mach-iop13xx/imu/dev.c    |  438 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-iop13xx/imu/imu.c    |   95 ++++++++
 include/asm-arm/arch-iop13xx/imu.h |  366 ++++++++++++++++++++++++++++++
 8 files changed, 1218 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-iop13xx/Kconfig b/arch/arm/mach-iop13xx/Kconfig
index 40c2d68..27c1c2c 100644
--- a/arch/arm/mach-iop13xx/Kconfig
+++ b/arch/arm/mach-iop13xx/Kconfig
@@ -16,5 +16,7 @@ config MACH_IQ81340MC
 	  Say Y here if you want to support running on the Intel IQ81340MC
 	  evaluation kit.
 
+source "arch/arm/mach-iop13xx/imu/Kconfig"
+
 endmenu
 endif
diff --git a/arch/arm/mach-iop13xx/Makefile b/arch/arm/mach-iop13xx/Makefile
index 4185e05..7937d73 100644
--- a/arch/arm/mach-iop13xx/Makefile
+++ b/arch/arm/mach-iop13xx/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_IOP13XX) += pci.o
 obj-$(CONFIG_ARCH_IOP13XX) += io.o
 obj-$(CONFIG_MACH_IQ81340SC) += iq81340sc.o
 obj-$(CONFIG_MACH_IQ81340MC) += iq81340mc.o
+obj-$(CONFIG_IOP_IMU) += imu/
diff --git a/arch/arm/mach-iop13xx/imu/Kconfig b/arch/arm/mach-iop13xx/imu/Kconfig
new file mode 100644
index 0000000..ee49b37
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/Kconfig
@@ -0,0 +1,19 @@
+#
+# IOP13xx IMU Support
+#
+
+menu "IOP13XX IMU Support"
+
+config IOP_IMU
+	tristate "IOP IMU support"
+	depends on EXPERIMENTAL
+	---help---
+	This includes support functions for the IMU.
+
+config IOP_IMU_DEV
+	tristate "IOP IMU char driver"
+	depends on IOP_IMU
+	---help---
+	This is a char driver that passes messages throught the IMU.
+
+endmenu
diff --git a/arch/arm/mach-iop13xx/imu/Makefile b/arch/arm/mach-iop13xx/imu/Makefile
new file mode 100644
index 0000000..e149b07
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_IOP_IMU)      += imu_layer.o
+obj-$(CONFIG_IOP_IMU_DEV)  += dev.o
+imu_layer-objs	:= common.o imu.o
diff --git a/arch/arm/mach-iop13xx/imu/common.c b/arch/arm/mach-iop13xx/imu/common.c
new file mode 100644
index 0000000..1f9628e
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/common.c
@@ -0,0 +1,294 @@
+/*
+ * arch/arm/mach-iop13xx/imu/common.c
+ *
+ * Interface functions for comunication using IMU hardware on the IOP1342
+ *
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * Author: Greg Tucker <greg.b.tucker@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/arch/imu.h>
+
+imu_handler *imu_irq_table[NR_IMU_DOORBELLS + NR_IMU_QUEUE_IRQS];
+struct imu_queue_params imu_queue[NR_IMU_QUEUES];
+
+
+/**
+ ****************************************************************************
+ * @iop_doorbell_reg_callback
+ * @brief
+ *   Register the callback function for a particular doorbell.
+ * @param  IN: int doorbell - doorbell number
+ * @param  IN: void * callback(void) - pointer to callback function
+ * @return NONE
+ *****************************************************************************/
+int iop_doorbell_reg_callback(int doorbell, void (*callback) (int))
+{
+	if ((doorbell >= 0) && (doorbell <= IMU_DB_RQ3NE)) {
+		imu_irq_table[doorbell] = (imu_handler *) callback;
+		return 0;
+	} else
+		return 1;
+}
+
+
+/**
+ ****************************************************************************
+ * @iop_queue_init
+ * @brief
+ *   Initialize circular queue.
+ *
+ *   Sets up a circular queue with memory for the queue buffers and
+ *   callback function for received messages.  Caller must first
+ *   allocate memory of appropriate size and type for the queue.  The
+ *   queue is of fixed size (msg_size * num_items) bytes.
+ *
+ *   @todo List restrictions on the memory type (dma-able, etc.)
+ *
+ * @param  IN: int queueid - queue identifier
+ * @param  IN: void * phys_base - physical base pointer to pre-allocated memory
+ * @param  IN: void * virt_base - virtual base pointer to pre-allocated memory
+ * @param  IN: int msg_size - size of fixed messages in bytes
+ * @param  IN: int num_items - max number of items in the queue
+ * @param  IN: void * callback(int) - pointer to callback function
+ * @param  IN: void * error_callback(int) - pointer to error function
+ * @return 0: success
+ *****************************************************************************/
+int
+iop_queue_init(int queueid, void *phys_base, void *virt_base, int msg_size,
+	       int num_items, void (*rcd_callback) (int),
+	       void (*error_callback) (int))
+{
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	if (queueid > 3 || num_items > ((1 << 16) - 1))
+		return 1;
+
+	/* init send queue pointers */
+	queue_hw->sqcr = (1 << 31)	/* reset send queue get/put pointers */
+	    |num_items;		/* set size */
+	queue_hw->rqcr = (1 << 31);	/* reset receive queue get/put pointers */
+	queue_hw->sqlbar = (int)phys_base;
+	queue_hw->squbar = 0;	/* assuming 32 bit address space */
+
+	imu_queue[queueid].txbase = virt_base;
+	imu_queue[queueid].msg_size = msg_size;
+	imu_queue[queueid].items = num_items;
+	imu_queue[queueid].alloc = queue_hw->sqpg >> 16;	/* alloc=get */
+
+	/* Register callback */
+	imu_irq_table[NR_IMU_DOORBELLS - 1 +
+		      (IMU_DB_RQ0NE - IMU_DB_QUEUE_IRQ_OFF) + (queueid * 2)] =
+	    (imu_handler *) rcd_callback;
+	/* Enable receive interrupt */
+	iop_doorbell_enable(IMU_DB_RQ0NE + (queueid * 2));
+
+	return 0;
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_allocate
+ * @brief
+ *   Return pointer to next free buffer queue.
+ * @param  IN: int queueid - queue identifier
+ * @return buffer pointer to fill with message
+ *****************************************************************************/
+void *iop_queue_allocate(int queueid)
+{
+	void *ret = NULL;
+	int alloc, get, items_m1, next_alloc;
+
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	if (queueid > 3)
+		return NULL;
+
+	alloc = queue->alloc;
+	get = queue_hw->sqpg >> 16;
+	items_m1 = queue->items - 1;
+	next_alloc = (alloc == items_m1) ? 0 : alloc + 1;
+
+	if (next_alloc != get) {	/* not full */
+		ret = (void *)(queue->txbase + (queue->msg_size * alloc));
+		queue->alloc = next_alloc;
+	}
+
+	return ret;
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_postmsg
+ * @brief
+ *   Post (send) all buffers up to message identified by message pointer.
+ * @param  IN: int queueid - queue identifier
+ * @param  IN: void * msg_adr - pointer to last message to post
+ * @return NONE
+ *****************************************************************************/
+int iop_queue_postmsg(int queueid, void *msg_adr)
+{
+	int offset, items, items_m1, put, alloc, index;
+
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	offset = (int)(msg_adr - queue->txbase);
+
+	items = queue->items;
+	items_m1 = items - 1;
+
+	/* Check if it is in range for this queue */
+	if (offset < 0 || offset > (queue->msg_size * items))
+		return 1;
+
+	put = queue_hw->sqpg & 0xffff;
+	alloc = queue->alloc;
+	index = offset / queue->msg_size;
+
+	/* todo: Check if allocated? */
+	/* don't post something already posted */
+
+	if (alloc >= put) {
+		if (index < put || index >= alloc)
+			return 1;	/* already posted or not allocated */
+	} else {
+		if (index < put && index >= alloc)
+			return 1;	/* already posted or not allocated */
+	}
+
+	queue_hw->sqpg = (index == items_m1) ? 0 : index + 1;
+
+	return 0;
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_rx_not_empty
+ * @brief
+ *   Check rx queue for not empty status.
+ * @param  IN: int queueid - queue identifier
+ * @return
+ *    0: rx queue empty
+ *    other: rx queue not empty
+ *****************************************************************************/
+int iop_queue_rx_not_empty(int queueid)
+{
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	int rqpg = queue_hw->rqpg;
+	int put = rqpg & 0xffff;
+	int get = rqpg >> 16;
+	return get - put;
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_tx_not_full
+ * @brief
+ *   Check tx queue for full status
+ * @param  IN: int queueid - queue identifier
+ * @return
+ *    0: tx queue full
+ *    other: rx queue not full
+ *****************************************************************************/
+int iop_queue_tx_not_full(int queueid)
+{
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	int get = queue_hw->sqpg >> 16;
+	int next_alloc = queue->alloc + 1;
+	next_alloc = (next_alloc == queue->items) ? 0 : next_alloc;
+
+	return next_alloc - get;
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_getmsg
+ * @brief
+ *   Get next available message.
+ *
+ *   Get a pointer to the next available message in the recieve queue.
+ *   Subsequent calls will return the same value until the message is freed.
+ *
+ * @param  IN: int queueid - queue identifier
+ * @return buffer pointer to next available message
+ *****************************************************************************/
+void *iop_queue_getmsg(int queueid)
+{
+
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	int rqpg = queue_hw->rqpg;
+	void *base = queue->rxbase;
+	int put = rqpg & 0xffff;
+	int get = rqpg >> 16;
+
+	if (!base || put == get)
+		return 0;
+
+	return base + (get * queue->msg_size);
+}
+
+/**
+ ****************************************************************************
+ * @iop_queue_rxfree
+ * @brief
+ *   Free a buffer in the receive queue that has already been processed.
+ *
+ *   Free the buffer that includes msg_addr.  Also frees all prior
+ *  messages in the queue.
+ *
+ * @param  IN: int queueid - queue identifier
+ * @param  IN: void * msg_addr - pointer to data within message to free
+ * @return buffer pointer to next available message
+ *****************************************************************************/
+int iop_queue_rxfree(int queueid, void *msg_adr)
+{
+	int rq_items, offset, index;
+
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	rq_items = queue_hw->rqcr & 0xffff;
+	offset = (int)(msg_adr - queue->rxbase);
+
+	/* Check if it is in range for this queue */
+	if (offset < 0 || offset > (queue->msg_size * rq_items)) {
+		return 1;
+	}
+
+	index = (offset / queue->msg_size) + 1;
+	index = (index == rq_items) ? 0 : index;
+	queue_hw->rqpg = index << 16;	/* update get pointer */
+
+	return 0;
+}
diff --git a/arch/arm/mach-iop13xx/imu/dev.c b/arch/arm/mach-iop13xx/imu/dev.c
new file mode 100644
index 0000000..38b1d80
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/dev.c
@@ -0,0 +1,438 @@
+/*
+ * arch/arm/mach-iop13xx/imu/iop1340-imu-dev.c
+ *
+ * Char driver for user-space access to IMU inter-core messaging.
+ *
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * Author: Greg Tucker <greg.b.tucker@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/jiffies.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/arch/imu.h>
+
+#define MODULE_VERS "1.0"
+#define MODULE_NAME "IMUdev"
+//#define IMU_MAJOR       245
+#define IMU_MAJOR         0
+#define IMU_MAX_MINORS    4
+#define IMU_FIRST_MINOR   1
+
+#define Q_MSG_SIZE   64
+#define Q_MSG_ITEMS  16
+#define Q_PHYS_BASE  (128*1024*1024)
+#define Q_SIZE       (Q_MSG_ITEMS*Q_MSG_SIZE)
+#define MSG_HEADER_SIZE   4
+
+#define IMU_WRITE_TIMEOUT 1000
+
+struct imu_dev {
+	struct cdev cdev;
+	wait_queue_head_t rq;
+	wait_queue_head_t wq;
+	size_t rq_leftover;
+	char *rq_leftover_ptr;
+	atomic_t read_available;
+	atomic_t write_available;
+};
+
+static struct imu_dev imu[IMU_MAX_MINORS];
+static int imu_major = IMU_MAJOR;
+
+void queue_rq_callback(int queueid)
+{
+	struct imu_dev *imui = &imu[queueid];
+
+	pr_debug("queue_rq_callback %d\n", queueid);
+	wake_up_interruptible(&imui->rq);
+	iop_doorbell_disable(IMU_DB_RQ0NE + (queueid * 2));
+}
+
+void queue_wq_callback(int queueid)
+{
+	struct imu_dev *imui = &imu[queueid];
+
+	pr_debug("wq callback on not full q=%d\n", queueid);
+	wake_up_interruptible(&imui->wq);
+	iop_doorbell_disable(IMU_DB_SQ0NF + (queueid * 2));
+}
+
+extern struct imu_queue_params imu_queue[];
+
+/*
+ * init_callback only called on the first rx to map the base
+ */
+
+#define has_overlap(a,b,c,d) (((c<=a) && (d>a))||((c<b) && (d>=b))||((c>=a) && (d<=b)))
+
+void init_callback(int queueid)
+{
+	int i, qi_rxitems, err = 0;
+	struct imu_queue_params *qi;
+	struct imu_queue_params *queue = &imu_queue[queueid];
+	struct imu_queue *qi_hw;
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (queueid * sizeof(struct imu_queue)));
+
+	int phy_rxbase = queue_hw->rqlbar;
+	int rq_items = queue_hw->rqcr & 0xffff;
+
+	// check if overlap
+
+	for (i = 0; i < 4; i++) {
+		qi = &imu_queue[i];
+		qi_hw = (struct imu_queue *)
+		    (IMU_Q0_BASE + (i * sizeof(struct imu_queue)));
+		qi_rxitems = qi_hw->rqcr & 0xffff;
+
+		if (i != queueid &&	// check overlap with other registered rx
+		    qi->rxbase && qi->msg_size &&
+		    has_overlap(phy_rxbase,
+				phy_rxbase + (rq_items * qi->msg_size),
+				(int)qi->rxbase,
+				(int)qi->rxbase +
+				(qi_rxitems * qi->msg_size))) {
+			err = 1;
+		}
+
+		if (qi->txbase && qi->msg_size &&	// check overlap with tx queues
+		    has_overlap(phy_rxbase,
+				phy_rxbase + (rq_items * qi->msg_size),
+				(int)qi->txbase,
+				(int)qi->txbase + (qi->items * qi->msg_size))) {
+			err = 1;
+		}
+	}
+
+	if (err) {
+		printk(KERN_WARNING
+		       "overlap found in IMU rx queue request 0x%x\n",
+		       (int)phy_rxbase);
+	}
+
+	queue->rxbase = ioremap(phy_rxbase, rq_items * Q_MSG_SIZE);
+
+	// switch to regular callback and call
+	iop_doorbell_reg_callback(NR_IMU_DOORBELLS - 1 +
+				  (IMU_DB_RQ0NE - IMU_DB_QUEUE_IRQ_OFF) +
+				  (queueid * 2), queue_rq_callback);
+
+	printk
+	    ("init_callback registerd q=%d rxbase=0x%x rxphy=0x%x size=0x%x\n",
+	     queueid, (int)queue->rxbase, phy_rxbase, rq_items * Q_MSG_SIZE);
+	queue_rq_callback(queueid);
+}
+
+void error_callback(int queueid)
+{
+}
+
+static int imu_open(struct inode *inode, struct file *file)
+{
+	struct imu_dev *imui;
+	int queueid = iminor(file->f_dentry->d_inode);
+
+	imui = &imu[queueid];
+
+	switch (file->f_flags & O_ACCMODE) {
+	case O_RDWR:
+		if (!atomic_dec_and_test(&imui->read_available)) {
+			atomic_inc(&imui->read_available);
+			return -EBUSY;
+		}
+		if (!atomic_dec_and_test(&imui->write_available)) {
+			atomic_inc(&imui->write_available);
+			atomic_inc(&imui->read_available);
+			return -EBUSY;
+		}
+		break;
+	case O_WRONLY:
+		if (!atomic_dec_and_test(&imui->write_available)) {
+			atomic_inc(&imui->write_available);
+			return -EBUSY;
+		}
+		break;
+	case O_RDONLY:
+		if (!atomic_dec_and_test(&imui->read_available)) {
+			atomic_inc(&imui->read_available);
+			return -EBUSY;
+		}
+		break;
+
+	}
+	return 0;
+}
+
+static int imu_release(struct inode *inode, struct file *file)
+{
+	struct imu_dev *imui;
+	int queueid = iminor(file->f_dentry->d_inode);
+
+	imui = &imu[queueid];
+
+	switch (file->f_flags & O_ACCMODE) {
+	case O_RDWR:
+		atomic_inc(&imui->read_available);	// fall through
+	case O_WRONLY:
+		atomic_inc(&imui->write_available);
+		break;
+	case O_RDONLY:
+		atomic_inc(&imui->read_available);
+		break;
+	}
+	return 0;
+}
+
+static ssize_t
+imu_read(struct file *file, char __user * buf, size_t count, loff_t * f_pos)
+{
+	struct imu_dev *imui;
+	char *dat;
+	int queueid = iminor(file->f_dentry->d_inode);
+
+	imui = &imu[queueid];
+
+	pr_debug("imu_read count=%d ", count);
+
+	while (1) {
+		if (imui->rq_leftover) {
+			pr_debug("%d leftover ", imui->rq_leftover);
+			count = min(count, imui->rq_leftover);
+			if (copy_to_user(buf, imui->rq_leftover_ptr, count))
+				return -EFAULT;
+			imui->rq_leftover -= count;
+			pr_debug(" %d left \n", imui->rq_leftover);
+			if (imui->rq_leftover == 0)
+				iop_queue_rxfree(queueid,
+						 imui->rq_leftover_ptr);
+			imui->rq_leftover_ptr += count;
+			return count;
+		}
+
+		while (!iop_queue_rx_not_empty(queueid)) {
+			DECLARE_WAITQUEUE(wait, current);
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			pr_debug("imu_read: empty going to sleep\n");
+
+			add_wait_queue(&imui->rq, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+			iop_doorbell_enable(IMU_DB_RQ0NE + (queueid * 2));
+			schedule_timeout(IMU_WRITE_TIMEOUT);
+
+			remove_wait_queue(&imui->rq, &wait);
+			if (signal_pending(current)) {
+				return -ERESTARTSYS;
+			}
+		}
+
+		// new message
+		dat = iop_queue_getmsg(queueid);
+
+		pr_debug("imu_read: got a new message at 0x%x\n", (int)dat);
+		if (NULL == dat)
+			return -EFAULT;
+
+#if MSG_HEADER_SIZE > 0
+		imui->rq_leftover = *((int *)dat);
+#else
+		imui->rq_leftover = 4;
+#endif
+		if (imui->rq_leftover > Q_MSG_SIZE)
+			imui->rq_leftover = 0;
+
+		imui->rq_leftover_ptr = dat + MSG_HEADER_SIZE;
+
+		pr_debug("imu_read: msg size=%d\n", imui->rq_leftover);
+		if (!imui->rq_leftover)
+			iop_queue_rxfree(queueid, imui->rq_leftover_ptr);
+	}
+
+}
+static ssize_t
+imu_write(struct file *file, const char __user * buf, size_t count,
+	  loff_t * f_pos)
+{
+	void *msg;
+	struct imu_dev *imui;
+	int queueid = iminor(file->f_dentry->d_inode);
+
+	imui = &imu[queueid];
+
+	count = min(count, (size_t) (Q_MSG_SIZE - MSG_HEADER_SIZE));
+
+	while (NULL == (msg = iop_queue_allocate(queueid))) {
+		DECLARE_WAITQUEUE(wait, current);
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		pr_debug("imu_write sleeping\n");
+
+		add_wait_queue(&imui->wq, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		iop_doorbell_enable(IMU_DB_SQ0NF + (queueid * 2));
+		schedule_timeout(IMU_WRITE_TIMEOUT);
+
+		remove_wait_queue(&imui->wq, &wait);
+		if (signal_pending(current)) {
+			return -ERESTARTSYS;
+		}
+
+	}
+
+	if (copy_from_user(msg + MSG_HEADER_SIZE, buf, count))
+		return -EFAULT;
+#if  MSG_HEADER_SIZE > 0
+	*((int *)msg) = count;
+#endif
+
+	iop_queue_postmsg(queueid, msg);
+
+	pr_debug("imu_write sent q=%d count=%d msg=0x%x\n", queueid, count,
+		 (int)msg);
+	return count;
+}
+
+static unsigned int imu_poll(struct file *file, poll_table * wait)
+{
+	struct imu_dev *imui;
+	unsigned int mask = 0;
+	int queueid = iminor(file->f_dentry->d_inode);
+
+	imui = &imu[queueid];
+	poll_wait(file, &imui->rq, wait);
+	poll_wait(file, &imui->wq, wait);
+
+	if (iop_queue_rx_not_empty(queueid))
+		mask |= POLLIN | POLLRDNORM;
+	if (iop_queue_tx_not_full(queueid))
+		mask |= POLLOUT | POLLWRNORM;
+
+	return mask;
+}
+
+static struct file_operations imu_fops = {
+	.owner = THIS_MODULE,
+	.read = imu_read,
+	.write = imu_write,
+	.poll = imu_poll,
+	.open = imu_open,
+	.release = imu_release,
+};
+
+static int __init imu_dev_init(void)
+{
+	dev_t dev;
+	// struct class_simple *imu_class;
+	int err, i;
+	char *queue_base;
+
+	if (imu_major) {
+		dev = MKDEV(imu_major, 0);
+		err = register_chrdev_region(dev, IMU_MAX_MINORS, "imu");
+	} else {
+		err = alloc_chrdev_region(&dev, IMU_FIRST_MINOR,
+					  IMU_MAX_MINORS, "imu");
+		imu_major = MAJOR(dev);
+	}
+	if (err < 0) {
+		printk(KERN_WARNING "imu: can't get major %d\n", imu_major);
+		return err;
+	}
+	//todo: update imu_class = class_simple_create(THIS_MODULE, "imu");
+
+	for (i = IMU_FIRST_MINOR; i < IMU_MAX_MINORS; i++) {
+		cdev_init(&imu[i].cdev, &imu_fops);
+		if (cdev_add(&imu[i].cdev, MKDEV(imu_major, i), 1)) {
+			printk(KERN_WARNING "Error cdev_add imu%i\n", i);
+			continue;
+		}
+
+		imu[i].rq_leftover = 0;
+		atomic_set(&imu[i].read_available, 1);
+		atomic_set(&imu[i].write_available, 1);
+		init_waitqueue_head(&imu[i].rq);
+		init_waitqueue_head(&imu[i].wq);
+		imu_queue[i].rxbase = 0;
+
+		queue_base = ioremap(Q_PHYS_BASE + (i * Q_SIZE), Q_SIZE);
+		// todo: see about changing to one of following
+		// non-shared device tex.cb = 0x010.00
+		// shared device     tex.cb = 0x001.01
+
+		err = iop_queue_init(i,
+				     (void *)Q_PHYS_BASE + (i * Q_SIZE),
+				     queue_base,
+				     Q_MSG_SIZE,
+				     Q_MSG_ITEMS,
+				     init_callback, error_callback);
+		if (err) {
+			printk(KERN_WARNING "could not init imu queue %d\n", i);
+			continue;
+		}
+		iop_doorbell_reg_callback(NR_IMU_DOORBELLS - 1 +
+					  (IMU_DB_SQ0NF -
+					   IMU_DB_QUEUE_IRQ_OFF) + (i * 2),
+					  queue_wq_callback);
+		printk(KERN_INFO
+		       "IMU Queue %d initialized major=%d minor=%d base=0x%x\n",
+		       i, imu_major, i, (int)queue_base);
+
+	}
+
+	return 0;
+}
+
+static void __exit imu_dev_cleanup(void)
+{
+	int i;
+
+	for (i = IMU_FIRST_MINOR; i < IMU_MAX_MINORS; i++) {
+		cdev_del(&imu[i].cdev);
+		iop_doorbell_disable(IMU_DB_RQ0NE + (i * 2));
+		iop_doorbell_disable(IMU_DB_SQ0NF + (i * 2));
+		iounmap(imu_queue[i].txbase);
+		if (imu_queue[i].rxbase) {
+			iounmap(imu_queue[i].rxbase);
+			imu_queue[i].rxbase = 0;
+		}
+	}
+
+	//todo: update  class_simple_device_remove(MKDEV(imu_major, 0));
+	unregister_chrdev_region(MKDEV(imu_major, 0), IMU_MAX_MINORS);
+
+	printk(KERN_INFO "%s driver ver %s removed\n",
+	       MODULE_NAME, MODULE_VERS);
+}
+
+module_init(imu_dev_init);
+module_exit(imu_dev_cleanup);
+
+MODULE_AUTHOR("Greg Tucker");
+MODULE_DESCRIPTION("IMU dev interface");
diff --git a/arch/arm/mach-iop13xx/imu/imu.c b/arch/arm/mach-iop13xx/imu/imu.c
new file mode 100644
index 0000000..690fae8
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/imu.c
@@ -0,0 +1,95 @@
+/*
+ * arch/arm/mach-iop13xx/imu/iop1340-imu.c
+ *
+ * Support for IMU communication for the Intel IOP1340 chipset
+ *
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * Author: Greg Tucker <greg.b.tucker@intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <asm/arch/imu.h>
+
+extern imu_handler *imu_irq_table[];
+extern struct imu_queue_params imu_queue[];
+
+static irqreturn_t imu_interrupt(int irq, void *dev_id)
+{
+	unsigned int doorbell, queue;
+	unsigned int dbcr = *(volatile int *)IMU_DBCR;
+
+	pr_debug("got imu interrupt. IMU_DBCR=0x%x\n", dbcr);
+
+	do {
+		doorbell = dbcr >> 28;
+		queue = (dbcr >> 24) & 0xf;
+
+		if (doorbell != 0xf && imu_irq_table[doorbell]) {
+			imu_irq_table[doorbell] (doorbell);
+			*(volatile int *)IMU_DBCR = 1 << doorbell;	// clear status
+		}
+
+		if (queue != 0xf
+		    && imu_irq_table[(NR_IMU_DOORBELLS - 1) + queue])
+			imu_irq_table[(NR_IMU_DOORBELLS - 1) +
+				      queue] (queue / 2);
+
+		dbcr = *(volatile int *)IMU_DBCR;
+
+	} while ((dbcr >> 24) != 0xff);
+
+	return IRQ_HANDLED;
+}
+
+static int __init iop_imu_init(void)
+{
+	int i, err;
+
+	for (i = 0; i < NR_IMU_DOORBELLS + NR_IMU_QUEUE_IRQS; i++)
+		imu_irq_table[i] = 0;
+
+	err = request_irq(IRQ_IOP13XX_IMU, imu_interrupt,
+			  IRQF_DISABLED, "imu interrupt", NULL);
+
+	printk(KERN_INFO "IOP 8134x IMU driver\n");
+	return err;
+}
+
+static void __exit iop_imu_exit(void)
+{
+	free_irq(IRQ_IOP13XX_IMU, NULL);
+	return;
+}
+
+EXPORT_SYMBOL(iop_doorbell_reg_callback);
+EXPORT_SYMBOL(iop_queue_init);
+EXPORT_SYMBOL(iop_queue_allocate);
+EXPORT_SYMBOL(iop_queue_postmsg);
+EXPORT_SYMBOL(iop_queue_getmsg);
+EXPORT_SYMBOL(iop_queue_rxfree);
+EXPORT_SYMBOL(iop_queue_rx_not_empty);
+EXPORT_SYMBOL(iop_queue_tx_not_full);
+EXPORT_SYMBOL(imu_queue);
+
+module_init(iop_imu_init);
+module_exit(iop_imu_exit);
+
+MODULE_AUTHOR("Greg Tucker");
+MODULE_DESCRIPTION("IMU interface");
diff --git a/include/asm-arm/arch-iop13xx/imu.h b/include/asm-arm/arch-iop13xx/imu.h
new file mode 100644
index 0000000..5346bd9
--- /dev/null
+++ b/include/asm-arm/arch-iop13xx/imu.h
@@ -0,0 +1,366 @@
+/*
+ * include/asm-arm/arch-iop13xx/iop1340-imu.h
+ *
+ * IMU hardware support on IOP342
+ *
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * Author: Greg Tucker <greg.b.tucker@intel.com>
+ *
+ */
+
+#ifndef IOP34X_H
+#define IOP34X_H
+
+#include <asm/arch/iop13xx.h>
+
+typedef int imu_handler(int);
+
+
+int iop_queue_init(int queueid, void *phys_base, void *vert_base,
+		   int msg_size, int num_items,
+		   void (*rcd_callback) (int), void (*error_callback) (int));
+void *iop_queue_allocate(int queueid);
+int iop_queue_postmsg(int queueid, void *msg_adr);
+void *iop_queue_getmsg(int queueid);
+int iop_queue_rxfree(int queueid, void *msg_adr);
+int iop_queue_rx_not_empty(int queueid);
+int iop_queue_tx_not_full(int queueid);
+int iop_doorbell_reg_callback(int doorbell, void (*callback) (int));
+
+/* --------------------------------------------------------------------------
+ * IMU/IMM registers
+ */
+
+/* Reset cause status - RCSR */
+
+#define RCSR_READ()  \
+   ({unsigned _val_; asm volatile("mrc\tp6, 0, %0, c0, c1, 0" : "=r" (_val_));_val_;})
+#define RCSR_MU_RESET       (1<<21)
+#define RCSR_TMPI3_RESET    (1<<18)
+#define RCSR_TMPI2_RESET    (1<<15)
+#define RCSR_TMPI1_RESET    (1<<12)
+#define RCSR_TMPI0_RESET    (1<<9)
+#define RCSR_WATCHDOG_RESET (1<<5)
+#define RCSR_TARGETED_RESET (1<<4)
+#define RCSR_ID_RESET_MASK  (0xf)
+#define RCSR_SYSTEM_RESET_MASK (RCSR_MU_RESET | RCSR_TARGETED_RESET | RCSR_TMPI3_RESET | \
+				RCSR_TMPI2_RESET | RCSR_TMPI1_RESET | RCSR_TMPI0_RESET | \
+				RCSR_WATCHDOG_RESET | RCSR_TARGETED_RESET)
+
+/* #define is_sys_reset() ((RCSR_READ() & RCSR_SYSTEM_RESET_MASK) == 0)  currently  not working in simulator */
+#define is_sys_reset() 1
+
+/* Software Interrupt Generation Register - SINTGENR */
+
+#define SINTGENR_READ()  \
+   ({  unsigned _val_;asm volatile("mrc\tp6, 0, %0, c1, c1, 0" : "=r" (_val_));_val_;})
+#define SINTGENR_WRITE(val) \
+   ({ asm volatile("mcr\tp6, 0, %0, c1, c1, 0" : : "r" (val)); })
+
+/* Targeted Reset Register - TARRSTR */
+
+# define TARRSTR_WRITE(val) \
+   ({ asm volatile("mcr\tp6, 0, %0, c2, c1, 0" : : "r" (val)); })
+
+#ifdef linux
+# define IMU_BASE    IOP13XX_PMMR_VIRT_MEM_BASE
+#else
+# define IMU_BASE    0xffd80000
+#endif
+
+/* IMU doorbell registers */
+
+#define IMU_DBCR   (IMU_BASE+0xa00)
+#define IMU_DBER   (IMU_BASE+0xa04)
+#define IMU_DBAR   (IMU_BASE+0xa10)
+#define IMU_DBEOR  (IMU_BASE+0xa14)
+#define NR_IMU_DOORBELLS   14
+
+/* Conventions on doorbell use */
+
+#define IMU_RESET_DOORBELL  3
+
+/* IMU send queue */
+
+#define IMU_Q0_BASE (IMU_BASE+0xa20)
+#define IMU_Q1_BASE (IMU_BASE+0xa40)
+#define IMU_Q2_BASE (IMU_BASE+0xa60)
+#define IMU_Q3_BASE (IMU_BASE+0xa80)
+
+#define IMU_SQPG0    (IMU_Q0_BASE+0x0)
+#define IMU_SQCR0    (IMU_Q0_BASE+0x4)
+#define IMU_SQLBAR0  (IMU_Q0_BASE+0x8)
+#define IMU_SQUBAR0  (IMU_Q0_BASE+0xc)
+#define IMU_RQPG0    (IMU_Q0_BASE+0x10)
+#define IMU_RQCR0    (IMU_Q0_BASE+0x14)
+#define IMU_RQLBAR0  (IMU_Q0_BASE+0x18)
+#define IMU_RQUBAR0  (IMU_Q0_BASE+0x1c)
+
+#define IMU_SQPG1    (IMU_Q1_BASE+0x0)
+#define IMU_SQCR1    (IMU_Q1_BASE+0x4)
+#define IMU_SQLBAR1  (IMU_Q1_BASE+0x8)
+#define IMU_SQUBAR1  (IMU_Q1_BASE+0xc)
+#define IMU_RQPG1    (IMU_Q1_BASE+0x10)
+#define IMU_RQCR1    (IMU_Q1_BASE+0x14)
+#define IMU_RQLBAR1  (IMU_Q1_BASE+0x18)
+#define IMU_RQUBAR1  (IMU_Q1_BASE+0x1c)
+
+#define IMU_SQPG2    (IMU_Q2_BASE+0x0)
+#define IMU_SQCR2    (IMU_Q2_BASE+0x4)
+#define IMU_SQLBAR2  (IMU_Q2_BASE+0x8)
+#define IMU_SQUBAR2  (IMU_Q2_BASE+0xc)
+#define IMU_RQPG2    (IMU_Q2_BASE+0x10)
+#define IMU_RQCR2    (IMU_Q2_BASE+0x14)
+#define IMU_RQLBAR2  (IMU_Q2_BASE+0x18)
+#define IMU_RQUBAR2  (IMU_Q2_BASE+0x1c)
+
+#define IMU_SQPG3    (IMU_Q3_BASE+0x0)
+#define IMU_SQCR3    (IMU_Q3_BASE+0x4)
+#define IMU_SQLBAR3  (IMU_Q3_BASE+0x8)
+#define IMU_SQUBAR3  (IMU_Q3_BASE+0xc)
+#define IMU_RQPG3    (IMU_Q3_BASE+0x10)
+#define IMU_RQCR3    (IMU_Q3_BASE+0x14)
+#define IMU_RQLBAR3  (IMU_Q3_BASE+0x18)
+#define IMU_RQUBAR3  (IMU_Q3_BASE+0x1c)
+#define NR_IMU_QUEUES       3
+
+/* IMU queue interrupts */
+
+#define IMU_DB_SQ0NF 16
+#define IMU_DB_RQ0NE 17
+#define IMU_DB_SQ1NF 18
+#define IMU_DB_RQ1NE 19
+#define IMU_DB_SQ2NF 20
+#define IMU_DB_RQ2NE 21
+#define IMU_DB_SQ3NF 22
+#define IMU_DB_RQ3NE 23
+#define IMU_DB_QUEUE_IRQ_OFF 16
+#define NR_IMU_QUEUE_IRQS     8
+
+#ifndef __ASSEMBLER__
+struct imu_queue {
+	unsigned int sqpg;	/* Send queue put/get register */
+	unsigned int sqcr;	/* Send queue control register */
+	unsigned int sqlbar;	/* Send queue lower base address reg */
+	unsigned int squbar;	/* Send queue upper base address reg */
+	unsigned int rqpg;	/* Receive queue put/get register */
+	unsigned int rqcr;	/* Receive queue control register */
+	unsigned int rqlbar;	/* Receive queue lower base address reg */
+	unsigned int rqubar;	/* Receive queue upper base address reg */
+};
+
+struct imu_queue_params {
+	void *txbase;
+	void *rxbase;
+	int alloc;
+	int msg_size;
+	int items;
+};
+#endif
+
+/* Test and set semaphore registers */
+
+#define IMU_TSR_BASE (IMU_BASE+0xb00)
+#define IMU_TSR(x)   ((volatile char*)(IMU_TSR_BASE+x))
+
+/* Some conventions on the semaphores */
+
+#define HW_MUTEX_FLASH_READ_CORE0 0
+#define HW_MUTEX_FLASH_READ_CORE1 1
+
+/**
+ ****************************************************************************
+ * @iop_doorbell_ring
+ * @brief
+ *   Ring specified hw doorbell.
+ *
+ *   Will cause an interrupt on the other processor if enabled.
+ *
+ * @param IN: int doorbell - doorbell number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_ring(int doorbell)
+{
+	*((volatile int *)IMU_DBAR) = (1 << doorbell);
+}
+
+/**
+ ****************************************************************************
+ * @iop_wait_on_doorbell
+ * @brief
+ *   Spin until doorbell asserted.
+ * @param  IN: int doorbell - doorbell number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_wait_on_doorbell(int doorbell)
+{
+	while (0 == (*((volatile int *)IMU_DBCR) & (1 << doorbell))) ;
+}
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_clear_status
+ * @brief
+ *   Clear doorbell status bit so future doorbells can interrupt.
+ * @param  IN: int doorbell - doorbell number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_clear_status(int doorbell)
+{
+	*((volatile int *)IMU_DBCR) = (1 << doorbell) & ((1 << 16) - 1);
+}
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_check_status
+ * @brief
+ *   Check if doorbell is active.
+ * @param  IN: int doorbell - doorbell number
+ * @return
+ *   0: doorbell not active
+ *   other: doorbell active
+ *****************************************************************************/
+static inline int iop_doorbell_check_status(int doorbell)
+{
+	return *((volatile int *)IMU_DBCR) & (1 << doorbell);
+}
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_enable
+ * @brief
+ *   Enable this doorbell to interrupt processor.
+ * @param  IN: int doorbell - doorbell number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_enable(int doorbell)
+{
+	*((volatile int *)IMU_DBER) |= (1 << doorbell);
+}
+
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_disable
+ * @brief
+ *   Disable particular doorbell.
+ * @param  IN: int doorbell - doorbell number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_disable(int doorbell)
+{
+	*((volatile int *)IMU_DBER) &= ~(1 << doorbell);
+}
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_mask
+ * @brief
+ *   Mask/Disable doorbells corresponding to mask.
+ * @param  IN: int mask - vector of doorbells to disable
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_mask(int mask)
+{
+	*((volatile int *)IMU_DBER) &= ~(mask);
+}
+
+/**
+ ******************************************************************************
+ * @iop_doorbell_unmask
+ * @brief
+ *   Unmask/Enable doorbells corresponding to mask.
+ * @param  IN: int mask - vector of doorbells to enable
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_doorbell_unmask(int mask)
+{
+	*((volatile int *)IMU_DBER) &= mask;
+}
+
+/**
+ ******************************************************************************
+ * @iop_mutex_lock
+ * @brief
+ *   Block until mutex is granted.
+ *
+ *   Checks if current core has been granted access to the hw mutex.
+ *   Does not check if it's the current thread that has access or
+ *   another thread on the same processor.  Only binary mutex is
+ *   supported.
+ *
+ * @param  IN: mutex - mutex number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_mutex_lock(int mutex)
+{
+	int trymutex = *IMU_TSR(mutex);
+	int corenum = (1 << iop13xx_cpu_id());
+
+	while (trymutex != 0 && trymutex != corenum) {
+		trymutex = *IMU_TSR(mutex);
+	}
+}
+
+/**
+ ****************************************************************************
+ * @iop_mutex_trylock
+ * @brief
+ *   Non-blocking attempt to get mutex.
+ *
+ *   Checks if current core has been granted access to the hw mutex.
+ *   Does not check if it's the current thread that has access or
+ *   another thread on the same processor.  Only binary mutex is
+ *   supported.
+ *
+ * @param IN: int mutex - mutex number
+ * @return
+ *   0: success
+ *   1: other core has lock
+ *****************************************************************************/
+static inline int iop_mutex_trylock(int mutex)
+{
+	int trymutex = *IMU_TSR(mutex);
+	int coreid = iop13xx_cpu_id();
+
+	if (trymutex == 0 || (trymutex ^ (1 << coreid)) == 0)
+		return 0;
+	else
+		return 1;
+}
+
+/**
+ ****************************************************************************
+ * @iop_mutex_unlock
+ * @brief
+ *   Unlock/free the given mutex.
+ *
+ *   Does not first check if the mutex is locked.  Assumes that the
+ *   calling thread ownes the mutex.  Currently only binary mutex is
+ *   supported so does not keep a lock count.
+ *
+ * @param  IN: int mutex - mutex number
+ * @return NONE
+ *****************************************************************************/
+static inline void iop_mutex_unlock(int mutex)
+{
+	/* assert(iop_mutex_trylock(mutex)); */
+	*IMU_TSR(mutex) = 0x0;
+}
+
+
+#endif				/* IOP34X_H */

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2.6.20 2/2] iop13xx: imu scsi driver
  2007-02-13 16:45 [PATCH 2.6.20 0/2] imu-scsi for iop13xx Dan Williams
  2007-02-13 16:47 ` [PATCH 2.6.20 1/2] iop13xx: add base support for the imu Dan Williams
@ 2007-02-13 16:47 ` Dan Williams
  1 sibling, 0 replies; 3+ messages in thread
From: Dan Williams @ 2007-02-13 16:47 UTC (permalink / raw)
  To: linux, James.Bottomley; +Cc: greg.b.tucker, linux-scsi, linux-arm.kernel

From: Greg Tucker <greg.b.tucker@intel.com>

Enable Linux to access the other core as if it were a scsi target.

Made changes suggested by James Bottomley such as dma_map direction not
bidirectional, proper SCSI return conditons, reset handlers and cleanup.

Signed-off-by: Greg Tucker <greg.b.tucker@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---

 arch/arm/mach-iop13xx/imu/Kconfig |    7 
 drivers/scsi/Makefile             |    1 
 drivers/scsi/iop13xx-imu-scsi.c   |  607 +++++++++++++++++++++++++++++++++++++
 3 files changed, 615 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-iop13xx/imu/Kconfig b/arch/arm/mach-iop13xx/imu/Kconfig
index ee49b37..96d9d3a 100644
--- a/arch/arm/mach-iop13xx/imu/Kconfig
+++ b/arch/arm/mach-iop13xx/imu/Kconfig
@@ -16,4 +16,11 @@ config IOP_IMU_DEV
 	---help---
 	This is a char driver that passes messages throught the IMU.
 
+config IOP_IMU_SCSI
+	tristate "IOP IMU scsi driver"
+	depends on IOP_IMU
+	---help---
+	This is a low-level SCSI driver that passes SCSI commands
+	to core 2.
+
 endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index bd7c988..c1ccf57 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvscsi/
 obj-$(CONFIG_SCSI_HPTIOP)	+= hptiop.o
 obj-$(CONFIG_SCSI_STEX)		+= stex.o
 
+obj-$(CONFIG_IOP_IMU_SCSI)	+= iop13xx-imu-scsi.o
 obj-$(CONFIG_ARM)		+= arm/
 
 obj-$(CONFIG_CHR_DEV_ST)	+= st.o
diff --git a/drivers/scsi/iop13xx-imu-scsi.c b/drivers/scsi/iop13xx-imu-scsi.c
new file mode 100644
index 0000000..2d64183
--- /dev/null
+++ b/drivers/scsi/iop13xx-imu-scsi.c
@@ -0,0 +1,607 @@
+/*
+ * drivers/scsi/iop13xx-imu-scsi.c
+ *
+ * SCSI low-level driver that forwards messages through the IOP342 IMU hw to 
+ * the other core.
+ *
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * Author: Greg Tucker <greg.b.tucker@intel.com>
+ *
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <asm/arch/imu.h>
+
+#define MODULE_VERS    "1.1"
+#define MODULE_NAME    "IMUscsi"
+#define IMU_WW_NAME     (0xeeed)
+#define IMU_MAX_CMD_LEN 32
+#define Q_NUM           0
+#define Q_PHYS_BASE     (0xffe00000 + (512*1024))
+#define Q_MSG_SIZE      256
+#define Q_MSG_ITEMS     16
+
+/* Derived params */
+#define Q_SIZE          (Q_MSG_ITEMS*Q_MSG_SIZE)
+#define SGL_PER_CMD  ((Q_MSG_SIZE-IMU_MAX_CMD_LEN - 24)/sizeof(struct imu_sge))
+
+/* #define IMU_DEBUG */
+#ifdef IMU_DEBUG
+# define imu_debug(fmt,arg...) printk(MODULE_NAME ": " fmt,##arg)
+# define dbg_print_cmd(cmd)    scsi_print_command(cmd)
+#else
+# define imu_debug(fmt,arg...) do { } while (0)
+# define dbg_print_cmd(cmd)    do { } while (0)
+#endif
+#define imu_warn(fmt,arg...) printk(KERN_WARNING MODULE_NAME ": " fmt,##arg)
+
+/* #define IMU_SCSI_PROFILING */
+#ifdef IMU_SCSI_PROFILING
+unsigned imu_queue_commands = 0;
+unsigned imu_rx_callbacks = 0;
+unsigned imu_failed_alloc = 0;
+# define imu_scsi_prof(x) x
+#else
+# define imu_scsi_prof(x)
+#endif
+
+struct imu_scsidata {
+	struct Scsi_Host *host;
+	u32 connection_handle;
+};
+
+struct imu_scsidata *imu_scsi;
+
+struct imu_sgl {
+	u32 len;
+	u32 addr;
+	u32 last_sge;
+};
+
+struct imu_sge {
+	u32 flen;
+	u32 addr_l;
+	u32 addr_h;
+};
+
+enum fcodes {
+	IMU_FCODE_ERROR = 0,
+	IMU_FCODE_CMD,
+	IMU_FCODE_XFER,
+	IMU_FCODE_STATUS,
+	IMU_FCODE_RESP,
+	IMU_FCODE_MANAGE,
+	IMU_FCODE_CONNECT_REQ,
+	IMU_FCODE_CONNECT_RESP,
+	IMU_FCODE_DISCONNECT_REQ,
+	IMU_FCODE_DISCONNECT_RESP,
+	IMU_FCODE_CACHE_ALLOC_REQ,
+	IMU_FCODE_CACHE_ALLOC_RESP,
+	IMU_FCODE_LOG_REQ,
+	IMU_FCODE_LOG_RESP,
+};
+
+struct imu_cmd {
+	union {
+		struct {
+			unsigned short fcode;
+			unsigned char flags;
+			unsigned char reserved;
+		};
+		u32 head;
+	};
+	u64 array_context;
+	u64 app_context;
+	u32 connection_handle;
+	union {
+		union {
+			struct {
+				u64 lun;
+				u32 ctl;
+				u32 datalen;
+				unsigned char cdb[16];
+				union {
+					struct imu_sgl sgl;
+					struct imu_sge sge;
+				};
+			} cdb_cmd;
+			unsigned char cmd[IMU_MAX_CMD_LEN];
+		};		/* targ_cmd; */
+
+		struct {
+			u32 scsi_status;
+			u32 residual_count;
+			u32 response_code;
+			u16 reserved;
+			u16 senselen;
+			unsigned char sense_data[100];
+		} resp_cmd;
+
+		struct {
+			u64 lun;
+			u32 management_func;
+			u64 management_task;
+		} management_cmd;
+
+		struct {
+			u32 blocks;	/* cache alloc command */
+			u32 address[32];
+		} cache_cmd;
+
+		u32 status;	/* status command, connect/disconnect response */
+
+		u64 world_wide_name;	/* connect request command */
+	};
+};
+
+/* SGE command flags */
+#define FLAG_LAST_ELEMENT           (1<<31)
+
+/* Target Command message flags */
+#define FLAG_CMD_RECEIVER_MUST_XFER (1<<(22-16))
+#define FLAG_CMD_SGL_NOT_IN_MSG     (1<<(20-16))
+/* Data transfer command flags */
+#define FLAG_DATA_LAST_XFER         (1<<(23-16))
+#define FLAG_DATA_SGL_NOT_IN_MSG    (1<<(20-16))
+/* Command response flags */
+#define FLAG_RSP_RESIDUAL_UNDER     (1<<(22-16))
+#define FLAG_RSP_RESIDUAL_OVER      (1<<(21-16))
+#define FLAG_REP_CODE_VALID_DATA    (1<<(20-16))
+#define FLAG_REP_SENSE_VALID        (1<<(19-16))
+
+/* response msssage codes */
+enum imu_resp_codes {
+	IMU_RESP_SUCCESS = 0,
+	IMU_RESP_REJECT,
+	IMU_RESP_FAILED,
+	IMU_RESP_INVALID,
+};
+
+
+int imu_proc_info(struct Scsi_Host *host, char *buffer, char **start,
+		  off_t offset, int length, int in)
+{
+	int ret;
+	char *p = buffer;
+
+	if (in)
+		return 0;
+
+	p += sprintf(p, "iop13xx IMU SCSI driver ver " MODULE_VERS "\n");
+
+#ifdef IMU_SCSI_PROFILING
+	p += sprintf(p,
+		     "  queue-commands: %d\n"
+		     "  rx-callbacks:   %d\n"
+		     "  failed-q-alloc: %d\n",
+		     imu_queue_commands, imu_rx_callbacks, imu_failed_alloc
+		);
+#endif
+
+	*start = buffer + offset;
+	ret = p - buffer - offset;
+	if (ret > length)
+		ret = length;
+
+	return ret;
+}
+
+
+int imu_scsi_send_gen_msg(int fcode)
+{
+	struct imu_cmd *msg;
+
+	msg = iop_queue_allocate(Q_NUM);
+	if (msg == NULL) {
+		imu_warn("failed to allocate imu msg for disconnect\n");
+		imu_scsi_prof(imu_failed_alloc++);
+		return FAILED;
+	}
+
+	msg->fcode             = fcode;
+	msg->array_context     = 0;
+	msg->app_context       = 0;
+	msg->connection_handle = imu_scsi->connection_handle;
+	if (iop_queue_postmsg(Q_NUM, msg))
+		return FAILED;
+
+	return SUCCESS;
+}
+
+static int imu_queuecommand(struct scsi_cmnd *scp,
+			    void (*done) (struct scsi_cmnd *))
+{
+	struct imu_cmd *msg;
+	int i, use_sg;
+	struct scatterlist *sglist;
+	struct imu_sge *sge, *sge_i;
+
+	imu_debug("imu_queuecommand for id=%d\n", scp->device->id);
+	imu_scsi_prof(imu_queue_commands++);
+	dbg_print_cmd(scp);
+
+	if (scp->device->id != 0) {
+		scp->result = (DID_BAD_TARGET << 16);
+		done(scp);
+		return 0;
+	}
+
+	scp->scsi_done = done;
+
+	if (!imu_scsi->connection_handle) {
+		scp->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	msg = iop_queue_allocate(Q_NUM);
+	if (msg == NULL) {
+		imu_warn("failed to allocate imu msg\n");
+		imu_scsi_prof(imu_failed_alloc++);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	msg->fcode = IMU_FCODE_CMD;
+	msg->flags = FLAG_CMD_RECEIVER_MUST_XFER;
+	msg->array_context = 0;
+	msg->app_context = (u32) scp;
+	msg->connection_handle = imu_scsi->connection_handle;
+	msg->cdb_cmd.lun = scp->device->lun << 8;
+	msg->cdb_cmd.ctl = 0;
+	msg->cdb_cmd.datalen = scp->request_bufflen;
+	memcpy(msg->cdb_cmd.cdb, scp->cmnd, 16);
+
+
+	if (scp->sc_data_direction == DMA_NONE)
+		imu_debug("direction DMA_NONE\n");
+
+	else if (scp->use_sg == 0) {
+		dma_addr_t buff = 0;
+		if (NULL == scp->request_buffer)
+			imu_warn("got cmd with NULL request_buffer\n");
+		else
+			buff = dma_map_single(NULL, scp->request_buffer,
+					      scp->request_bufflen,
+					      scp->sc_data_direction);
+		if (buff == 0) {
+			imu_warn("null return from dma_map_single\n");
+			msg->flags = IMU_FCODE_STATUS;
+			msg->status = 0;
+			iop_queue_postmsg(Q_NUM, msg);
+			return 1;
+		}
+		sge = &(msg->cdb_cmd.sge);
+		sge->addr_l = buff;
+		sge->addr_h = 0;
+		sge->flen = scp->request_bufflen | FLAG_LAST_ELEMENT;
+
+		imu_debug("cmd msg with 1 sgl entry, addr=0x%x\n", buff);
+	}
+	else if (scp->use_sg > 0) {
+		sglist = (struct scatterlist *)scp->request_buffer;
+		if (NULL == sglist) {
+			imu_warn("got null scp->buffer\n");
+			use_sg = 0;
+		} else
+			use_sg = dma_map_sg(NULL, sglist, scp->use_sg, 
+					    scp->sc_data_direction);
+		if (use_sg == 0) {
+			/* Send error status message */
+			imu_warn("null return from dma_map_sg\n");
+			msg->flags = IMU_FCODE_STATUS;
+			msg->status = 0;
+			iop_queue_postmsg(Q_NUM, msg);
+			return 1;
+		}
+
+		sge = sge_i = &(msg->cdb_cmd.sge);
+
+		for (i = 0; i < use_sg; i++, sglist++, sge_i++) {
+			sge_i->addr_l = sg_dma_address(sglist);
+			sge_i->addr_h = 0;
+			sge_i->flen = sg_dma_len(sglist);
+		}
+		sge[use_sg - 1].flen |= FLAG_LAST_ELEMENT;
+
+		imu_debug("cmd msg with %d sgl entries, "
+			  "last flen=0x%x last addr=0x%x\n", use_sg, 
+			  sge[use_sg - 1].flen,
+			  sge[use_sg - 1].addr_l);
+
+	}
+
+	if (iop_queue_postmsg(Q_NUM, msg))
+		return SCSI_MLQUEUE_DEVICE_BUSY;
+
+	return 0;
+}
+
+
+static int imu_eh_host_reset(struct scsi_cmnd *cmd)
+{
+	imu_warn("Received eh host reset\n");
+
+	if (imu_scsi->connection_handle && 
+	    imu_scsi_send_gen_msg(IMU_FCODE_ERROR))
+		return SUCCESS;
+
+	return FAILED;
+}
+
+
+static struct scsi_host_template imu_scsi_template = {
+	.module = THIS_MODULE,
+	.proc_info = imu_proc_info,
+	.name = "iop13xx IMU SCSI",
+	.queuecommand = imu_queuecommand,
+	.eh_host_reset_handler = imu_eh_host_reset,
+	.can_queue = Q_MSG_ITEMS - 2,
+	.this_id = -1,
+	.cmd_per_lun = 2,
+	.sg_tablesize = SGL_PER_CMD,
+	.use_clustering = ENABLE_CLUSTERING,
+	.proc_name = "iop13xx-imu",
+};
+
+void queue_rq_scsi_callback(int queueid)
+{
+	struct imu_cmd *msg;
+	struct scsi_cmnd *scp;
+
+	imu_debug("queue_rq_scsi_callback on queue %d\n", queueid);
+	imu_scsi_prof(imu_rx_callbacks++);
+
+	msg = iop_queue_getmsg(queueid);
+	if (msg == NULL)
+		return;
+
+	switch (msg->fcode) {
+
+	case IMU_FCODE_RESP:
+
+		scp = (struct scsi_cmnd *)((u32) msg->app_context);
+		if (!scp) {
+			imu_warn("got an invalid scp pointer\n");
+			break;
+		}
+
+		switch (msg->resp_cmd.response_code) {
+
+		case IMU_RESP_SUCCESS:
+			scp->result = (DID_OK << 16);
+			break;
+		case IMU_RESP_REJECT:
+		case IMU_RESP_FAILED:
+			scp->result = (DID_ABORT << 16);
+			break;
+		case IMU_RESP_INVALID:
+		default:
+			scp->result = (DID_ERROR << 16);
+			imu_warn("got a non success resp command:%d\n",
+				 msg->resp_cmd.scsi_status);
+			break;
+		}
+
+		if (msg->flags & FLAG_REP_SENSE_VALID) {
+			memcpy(scp->sense_buffer, msg->resp_cmd.sense_data,
+			       msg->resp_cmd.senselen < SCSI_SENSE_BUFFERSIZE ?
+			       msg->resp_cmd.senselen : SCSI_SENSE_BUFFERSIZE);
+			imu_debug("sense data in command of len %d\n",
+				  msg->resp_cmd.senselen);
+#ifdef IMU_DEBUG
+			scsi_print_sense("IMUscsi", scp);
+#endif
+		}
+
+		if (scp->scsi_done)
+			scp->scsi_done(scp);
+		else
+			imu_warn("no scsi_done set in response cmd\n");
+		break;
+
+	case IMU_FCODE_CONNECT_RESP:
+		if (!msg->status)
+			imu_scsi->connection_handle = msg->connection_handle;
+		else
+			imu_warn("rejected or failed connection request");
+
+		break;
+
+	case IMU_FCODE_DISCONNECT_RESP:
+		imu_scsi->connection_handle = 0;
+		break;
+
+	case IMU_FCODE_CMD:
+	case IMU_FCODE_XFER:
+	case IMU_FCODE_MANAGE:
+	case IMU_FCODE_CONNECT_REQ:
+	case IMU_FCODE_DISCONNECT_REQ:
+	case IMU_FCODE_CACHE_ALLOC_REQ:
+	case IMU_FCODE_CACHE_ALLOC_RESP:
+	case IMU_FCODE_LOG_REQ:
+	case IMU_FCODE_LOG_RESP:
+	case IMU_FCODE_ERROR:
+	default:
+		imu_warn("got a bad or unhandled fcode response %d\n",
+			 msg->fcode);
+		break;
+	}
+
+	iop_queue_rxfree(queueid, msg);
+	return;
+
+}
+
+/* We need to access queue structure to look for overlap */
+extern struct imu_queue_params imu_queue[];
+
+void init_scsi_bh(struct work_struct *unused)
+{
+	struct imu_queue_params *queue = &imu_queue[Q_NUM];
+	struct imu_queue *queue_hw = (struct imu_queue *)
+	    (IMU_Q0_BASE + (Q_NUM * sizeof(struct imu_queue)));
+
+	int phy_rxbase = queue_hw->rqlbar;
+	int rq_items = queue_hw->rqcr & 0xffff;
+
+	queue->rxbase = ioremap(phy_rxbase, rq_items * Q_MSG_SIZE);
+	/* todo: see about changing to cacheable and invalidate before alloc */
+
+	/* switch to regular callback and call */
+	iop_doorbell_reg_callback(NR_IMU_DOORBELLS - 1 +
+				  (IMU_DB_RQ0NE - IMU_DB_QUEUE_IRQ_OFF) +
+				  (Q_NUM * 2), queue_rq_scsi_callback);
+
+	imu_debug("init_scsi_callback registerd "
+		  "q=%d rxbase=0x%x rxphy=0x%x size=0x%x\n",
+		  Q_NUM, (int)queue->rxbase, phy_rxbase, rq_items * Q_MSG_SIZE);
+	queue_rq_scsi_callback(Q_NUM);
+	iop_doorbell_enable(IMU_DB_RQ0NE + (Q_NUM * 2));
+}
+
+
+static DECLARE_WORK(init_imu_scsi_tq, init_scsi_bh);
+
+void init_scsi_callback(int queueid)
+{
+	iop_doorbell_disable(IMU_DB_RQ0NE + (queueid * 2));
+	schedule_work(&init_imu_scsi_tq);
+}
+
+void error_scsi_callback(int queueid)
+{
+}
+
+static int imu_attach(void)
+{
+	char *queue_base;
+	int err;
+
+	imu_debug("imu_attach\n");
+
+	imu_queue[Q_NUM].rxbase = 0;
+
+	queue_base = ioremap(Q_PHYS_BASE + (Q_NUM * Q_SIZE), Q_SIZE);
+	/* todo: see about changing to bufferable mem and dmb before post */
+
+	if (queue_base == NULL) {
+		imu_warn("could not ioremap region\n");
+		return -1;
+	}
+
+	err = iop_queue_init(Q_NUM,
+			     (void *)Q_PHYS_BASE + (Q_NUM * Q_SIZE),
+			     queue_base,
+			     Q_MSG_SIZE,
+			     Q_MSG_ITEMS,
+			     init_scsi_callback, error_scsi_callback);
+	if (err) {
+		imu_warn("could not init queue\n");
+		iounmap(queue_base);
+		return -1;
+	}
+
+	printk(KERN_INFO MODULE_NAME ": using queue %d base:0x%x phy:0x%x\n",
+	       Q_NUM, (int)queue_base, Q_PHYS_BASE + (Q_NUM * Q_SIZE));
+
+	return 0;
+}
+
+static int imu_detach(void)
+{
+
+	iop_doorbell_disable(IMU_DB_RQ0NE + (Q_NUM * 2));
+	iop_doorbell_disable(IMU_DB_SQ0NF + (Q_NUM * 2));
+	if (imu_queue[Q_NUM].txbase) {
+		iounmap(imu_queue[Q_NUM].txbase);
+		imu_queue[Q_NUM].txbase = 0;
+	}
+	if (imu_queue[Q_NUM].rxbase) {
+		iounmap(imu_queue[Q_NUM].rxbase);
+		imu_queue[Q_NUM].rxbase = 0;
+	}
+
+	return 0;
+}
+
+static int __init imu_scsi_init(void)
+{
+	struct Scsi_Host *host;
+	struct imu_scsidata *data;
+	struct imu_cmd *msg;
+	int ret;
+
+	if (imu_attach()) {
+		imu_warn("imu queue alloc failed\n");
+		return -1;
+	}
+
+	host = scsi_host_alloc(&imu_scsi_template, sizeof(struct imu_scsidata));
+	if (!host)
+		return -ENOMEM;
+
+	data = (struct imu_scsidata *)host->hostdata;
+	imu_scsi = data;
+	data->host = host;
+	data->connection_handle = 1;  /* should be 0; bug in current target has
+					 bad response to connection request */
+
+	/* Send connection request msg */
+	msg = iop_queue_allocate(Q_NUM);
+	if (msg == NULL) {
+		imu_warn("failed to allocate imu msg for connect\n");
+		imu_scsi_prof(imu_failed_alloc++);
+		return -1;
+	}
+	msg->fcode = IMU_FCODE_CONNECT_REQ;
+	msg->array_context = 0;
+	msg->app_context = 0;
+	msg->connection_handle = 0;
+	msg->world_wide_name = IMU_WW_NAME;
+	iop_queue_postmsg(Q_NUM, msg);
+
+	imu_debug("scsi_add_host\n");
+
+	ret = scsi_add_host(host, NULL);
+	if (!ret) {
+		scsi_scan_host(host);
+		imu_debug("scsi_scan_host complete\n");
+	}
+
+	return ret;
+}
+
+static void __exit imu_scsi_exit(void)
+{
+	struct Scsi_Host *host = imu_scsi->host;
+
+	scsi_remove_host(host);
+	scsi_host_put(host);
+	imu_scsi_send_gen_msg(IMU_FCODE_DISCONNECT_REQ);
+	imu_detach();
+	printk(KERN_INFO MODULE_NAME ": Detached\n");
+}
+
+module_init(imu_scsi_init);
+module_exit(imu_scsi_exit);
+
+MODULE_AUTHOR("Greg Tucker");
+MODULE_DESCRIPTION("iop13xx IMU SCSI driver");

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-02-13 16:47 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-02-13 16:45 [PATCH 2.6.20 0/2] imu-scsi for iop13xx Dan Williams
2007-02-13 16:47 ` [PATCH 2.6.20 1/2] iop13xx: add base support for the imu Dan Williams
2007-02-13 16:47 ` [PATCH 2.6.20 2/2] iop13xx: imu scsi driver Dan Williams

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).