public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/2] imu-scsi for iop13xx
@ 2007-01-24 18:22 dan.j.williams
  2007-01-24 18:24 ` [PATCH RFC 1/2] iop13xx: add base support for the imu Dan Williams
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: dan.j.williams @ 2007-01-24 18:22 UTC (permalink / raw)
  To: linux, James.Bottomley
  Cc: linux-arm-kernel, linux-scsi, dan.j.williams, greg.b.tucker

The imu (inter-processor messaging unit) on the iop13xx enables mailbox
style communication between the two cores on iop13xx.  Greg Tucker has
has wrapped this capability in a scsi driver which allows Linux to
interface with the other core as if it were a scsi disk target.  I would
like to submit it for 2.6.21 consideration.  As it is an
ARM-platform-specific driver does it fall under Russell's purview, or
James' since it is a scsi driver.

Regards,
Dan

Greg Tucker (2):
      iop13xx: add base support for the imu
      iop13xx: imu scsi driver

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

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

* [PATCH RFC 1/2] iop13xx: add base support for the imu
  2007-01-24 18:22 [PATCH RFC 0/2] imu-scsi for iop13xx dan.j.williams
@ 2007-01-24 18:24 ` Dan Williams
  2007-01-24 18:24 ` [PATCH RFC 2/2] iop13xx: imu scsi driver Dan Williams
  2007-01-25 21:15 ` [PATCH RFC 0/2] imu-scsi for iop13xx James Bottomley
  2 siblings, 0 replies; 6+ messages in thread
From: Dan Williams @ 2007-01-24 18:24 UTC (permalink / raw)
  To: linux, James.Bottomley
  Cc: linux-arm-kernel, linux-scsi, dan.j.williams, greg.b.tucker

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

The interprocessor messaging unit supports mailbox style communication
between the two Xscale cores on iop342.
    
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         |  486 ++++++++++++++++++++++++++++
 arch/arm/mach-iop13xx/imu/dev.c            |  438 +++++++++++++++++++++++++
 arch/arm/mach-iop13xx/imu/imu.c            |  151 +++++++++
 include/asm-arm/arch-iop13xx/iop13xx-imu.h |  207 ++++++++++++
 8 files changed, 1307 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..f89fb53
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_IOP_IMU)      += common.o imu.o
+obj-$(CONFIG_IOP_IMU_DEV)  += dev.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..2d9c311
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/common.c
@@ -0,0 +1,486 @@
+/*
+ * arch/arm/mach-iop13xx/imu/iop1340-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>
+ *
+ */
+
+#ifdef linux
+#  include <linux/module.h>
+#  include <linux/kernel.h>
+#  include <asm/arch/iop13xx-imu.h>
+#else
+#  include "iop1340-imu-common.h"
+#endif
+
+imu_handler *imu_irq_table[NR_IMU_DOORBELLS + NR_IMU_QUEUE_IRQS];
+struct imu_queue_params imu_queue[NR_IMU_QUEUES];
+
+/**
+ ****************************************************************************
+ * @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
+ *****************************************************************************/
+
+/* #define iop_doorbell_ring(doorbell)  do {*((volatile int*)IMU_DBAR) = (1<<doorbell);} while(0) */
+
+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
+ *****************************************************************************/
+
+/*#define iop_wait_on_doorbell(doorbell)  do {while ( 0 == (*((volatile int*)IMU_DBCR) & (1<<doorbell)) ) ;} while (0) */
+
+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
+ *****************************************************************************/
+
+/* #define iop_doorbell_clear_status(doorbell) do {*((volatile int*)IMU_DBCR) = (1<<doorbell) & ((1<<16)-1);} while(0) */
+
+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
+ *****************************************************************************/
+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
+ *****************************************************************************/
+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
+ *****************************************************************************/
+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
+ *****************************************************************************/
+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
+ *****************************************************************************/
+void iop_doorbell_unmask(int mask)
+{
+	*((volatile int *)IMU_DBER) &= mask;
+}
+
+/**
+ ****************************************************************************
+ * @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_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
+ *****************************************************************************/
+void iop_mutex_lock(int mutex)
+{
+	int trymutex = *IMU_TSR(mutex);
+	int corenum = (1 << CIDR_READ());
+
+	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
+ *****************************************************************************/
+int iop_mutex_trylock(int mutex)
+{
+	int trymutex = *IMU_TSR(mutex);
+	int coreid = CIDR_READ();
+
+	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
+ *****************************************************************************/
+void iop_mutex_unlock(int mutex)
+{
+	/* assert(iop_mutex_trylock(mutex)); */
+	*IMU_TSR(mutex) = 0x0;
+}
+
+/**
+ ****************************************************************************
+ * @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;
+
+	/* todo: put mutex around alloc update */
+	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;
+	}
+	/* todo: unlock mutex on 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..b73af6b
--- /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/iop13xx-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..6626486
--- /dev/null
+++ b/arch/arm/mach-iop13xx/imu/imu.c
@@ -0,0 +1,151 @@
+/*
+ * 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>
+#define __KERNEL_SYSCALLS__
+#include <linux/reboot.h>
+#include <linux/unistd.h>	/* for execve */
+#include <asm/arch/iop13xx-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, struct pt_regs *regs)
+{
+	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 void deferred_reset(void *dummy)
+{
+	static int in_shutdown = 0;
+	static char *envp[] = {
+		"HOME=/", "TERM=linux",
+		"PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL
+	};
+	char *argv[] = {
+		"/sbin/shutdown", "-r", "now", NULL
+	};
+
+	// todo: start watchdog timer
+
+	if (!in_shutdown) {
+		in_shutdown = 1;
+		printk(KERN_CRIT "IMU reset: Shutting down the system now.\n");
+		if (0 > execve("/sbin/shutdown", argv, envp))
+			printk("IMU reset: Could not use /sbin/shutdown\n");
+		else
+			return;
+	}
+
+	printk("IMU reset: Starting hard reset\n");
+	//emergency_sync();
+	//kill_proc(1, SIGINT, 1);
+	kernel_restart(NULL);
+}
+
+void restart_callback(int irq)
+{
+	static DECLARE_WORK(reset_work, deferred_reset, NULL);
+
+	printk("\nIMU reset: Got restart doorbell\n");
+	schedule_work(&reset_work);
+	return;
+}
+
+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,
+			  SA_INTERRUPT, "imu interrupt", NULL);
+
+	iop_doorbell_reg_callback(IMU_RESET_DOORBELL, restart_callback);
+	iop_doorbell_enable(IMU_RESET_DOORBELL);
+
+	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_ring);
+EXPORT_SYMBOL(iop_wait_on_doorbell);
+EXPORT_SYMBOL(iop_doorbell_clear_status);
+EXPORT_SYMBOL(iop_doorbell_check_status);
+EXPORT_SYMBOL(iop_doorbell_enable);
+EXPORT_SYMBOL(iop_doorbell_disable);
+EXPORT_SYMBOL(iop_doorbell_mask);
+EXPORT_SYMBOL(iop_doorbell_unmask);
+EXPORT_SYMBOL(iop_doorbell_reg_callback);
+EXPORT_SYMBOL(iop_mutex_lock);
+EXPORT_SYMBOL(iop_mutex_trylock);
+EXPORT_SYMBOL(iop_mutex_unlock);
+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/iop13xx-imu.h b/include/asm-arm/arch-iop13xx/iop13xx-imu.h
new file mode 100644
index 0000000..f7ecd6a
--- /dev/null
+++ b/include/asm-arm/arch-iop13xx/iop13xx-imu.h
@@ -0,0 +1,207 @@
+/*
+ * 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
+
+#ifndef NULL
+# define NULL (void *) 0
+#endif
+
+typedef int imu_handler(int);
+
+void iop_mutex_lock(int mutex);
+int iop_mutex_trylock(int mutex);
+void iop_mutex_unlock(int mutex);
+
+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);
+
+int iop_doorbell_reg_callback(int doorbell, void (*callback) (int));
+void iop_doorbell_clear_status(int doorbell);
+int iop_doorbell_check_status(int doorbell);
+void iop_doorbell_enable(int doorbell);
+void iop_doorbell_disable(int doorbell);
+void iop_doorbell_mask(int doorbell);
+void iop_doorbell_unmask(int doorbell);
+void iop_doorbell_ring(int doorbell);
+inline void iop_wait_on_doorbell(int doorbell);
+
+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);
+
+/* Core ID - CIDR */
+
+#define CIDR_READ() \
+ ({unsigned _val_; asm volatile("mrc\tp6, 0, %0, c0, c0, 0" : "=r" (_val_)); _val_;})
+#define COREID0 0
+#define COREID1 1
+
+/* --------------------------------------------------------------------------
+ * 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
+
+#endif				/* IOP34X_H */

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

* [PATCH RFC 2/2] iop13xx: imu scsi driver
  2007-01-24 18:22 [PATCH RFC 0/2] imu-scsi for iop13xx dan.j.williams
  2007-01-24 18:24 ` [PATCH RFC 1/2] iop13xx: add base support for the imu Dan Williams
@ 2007-01-24 18:24 ` Dan Williams
  2007-01-25 21:15   ` James Bottomley
  2007-01-25 21:15 ` [PATCH RFC 0/2] imu-scsi for iop13xx James Bottomley
  2 siblings, 1 reply; 6+ messages in thread
From: Dan Williams @ 2007-01-24 18:24 UTC (permalink / raw)
  To: linux, James.Bottomley
  Cc: linux-arm-kernel, linux-scsi, dan.j.williams, greg.b.tucker

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

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

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   |  623 +++++++++++++++++++++++++++++++++++++
 3 files changed, 631 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..d32097b
--- /dev/null
+++ b/drivers/scsi/iop13xx-imu-scsi.c
@@ -0,0 +1,623 @@
+/*
+ * drivers/scsi/iop13xx-imu-scsi.c
+ *
+ * SCSI low-level driver that forwards messages through the IOP342 IMU hw to 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/iop13xx-imu.h>
+
+#define MODULE_VERS    "1.0"
+#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
+//#define IMU_DEBUG
+
+/* 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))
+
+#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)
+
+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 {
+	//unsigned char flags;
+	//unsigned int  len : 24;
+	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");
+
+	*start = buffer + offset;
+	ret = p - buffer - offset;
+	if (ret > length)
+		ret = length;
+
+	return ret;
+}
+
+#if 0
+void iop_loopback_test(struct scsi_cmnd *scp)
+{
+
+	switch (scp->cmnd[0]) {
+	case READ_10:
+	case READ_6:
+	case WRITE_10:
+	case WRITE_6:
+	case MODE_SENSE_10:
+	case READ_CAPACITY:
+	case TEST_UNIT_READY:
+	default:
+		break;
+	}
+	memzero(scp->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+	scp->result = (DID_OK << 16);
+	scp->result = 0;
+	if (scp->scsi_done)
+		scp->scsi_done(scp);
+}
+#else
+# define iop_loopback_test(x)
+#endif
+
+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);
+	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);
+		done(scp);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	msg = iop_queue_allocate(Q_NUM);
+	if (msg == NULL) {
+		imu_warn("failed to allocate imu msg\n");
+		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;	// todo: fill out
+	msg->cdb_cmd.datalen = scp->request_bufflen;
+	memcpy(msg->cdb_cmd.cdb, scp->cmnd, 16);
+
+	switch (scp->cmnd[0]) {
+	case TEST_UNIT_READY:
+		break;
+	case READ_10:
+	case READ_6:
+	case WRITE_10:
+	case WRITE_6:
+	case READ_CAPACITY:
+	default:
+		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,
+						   DMA_BIDIRECTIONAL);
+			if (buff == 0) {
+				imu_warn("null return from dma_map_single\n");
+				msg->flags = IMU_FCODE_STATUS;
+				msg->status = 0;	//todo: what is error status?
+				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;
+
+			// This shouldn't be necessary
+			//dma_sync_single(NULL, buff, scp->request_bufflen, DMA_BIDIRECTIONAL);
+			imu_debug("cmd msg with 1 sgl entry, addr=0x%x\n",
+				  buff);
+		}
+		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,
+					       DMA_BIDIRECTIONAL);
+			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;	//todo: need to send error status
+				iop_queue_postmsg(Q_NUM, msg);
+				return 1;
+			}
+			// This shouldn't be necessary
+			//dma_sync_sg(NULL, sglist, use_sg, DMA_BIDIRECTIONAL);
+
+			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);
+
+		}
+
+		break;
+	}
+
+	iop_queue_postmsg(Q_NUM, msg);
+	iop_loopback_test(scp);
+
+	return 0;
+}
+
+static int imu_eh_abort(struct scsi_cmnd *cmd)
+{
+	imu_warn("Received eh command abort\n");
+	//dbg_print_cmd(cmd);
+	return SUCCESS;
+}
+
+static int imu_eh_device_reset(struct scsi_cmnd *cmd)
+{
+	int target = cmd->device->id;
+	imu_warn("Received eh device reset for target %d\n", target);
+	return SUCCESS;
+}
+
+static int imu_eh_host_reset(struct scsi_cmnd *cmd)
+{
+	imu_warn("Received eh host reset\n");
+	return SUCCESS;
+}
+
+static int imu_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+	imu_warn("Received eh bus reset\n");
+	return SUCCESS;
+}
+
+static struct scsi_host_template imu_scsi_template = {
+	.module = THIS_MODULE,
+	.proc_info = imu_proc_info,
+	.name = "iop13xx IMU SCSI",
+	.queuecommand = imu_queuecommand,
+	.eh_abort_handler = imu_eh_abort,
+	.eh_device_reset_handler = imu_eh_device_reset,
+	.eh_host_reset_handler = imu_eh_host_reset,
+	.eh_bus_reset_handler = imu_eh_bus_reset,
+	.can_queue = Q_MSG_ITEMS - 2,
+	.this_id = -1,
+	.cmd_per_lun = 2,
+	.sg_tablesize = SGL_PER_CMD,	/* 1, SG_ALL, SG_NONE */
+	.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);
+	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;
+		}
+		//dbg_print_cmd(scp);
+
+		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);
+			printk("got sense data of %d bytes\n",
+			       msg->resp_cmd.senselen);
+			scsi_print_sense("IMUscsi", scp);
+		}
+
+		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_callback(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 phy_rxbase = queue_hw->rqlbar;
+	int rq_items = queue_hw->rqcr & 0xffff;
+
+	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_scsi_callback);
+
+	imu_debug
+	    ("init_scsi_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_scsi_callback(queueid);
+}
+
+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 one of following
+	// non-shared device tex.cb = 0x010.00
+	// shared device     tex.cb = 0x001.01
+
+	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));
+	iounmap(imu_queue[Q_NUM].txbase);
+
+	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;	// todo:change to 0 after connect request added
+
+	/* Send connection request msg */
+	msg = iop_queue_allocate(Q_NUM);
+	if (msg == NULL) {
+		imu_warn("failed to allocate imu msg for connect\n");
+		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);	// dev?
+	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);
+
+	/* Send disconnect request message */
+	/* Removed for debug
+	   msg = iop_queue_allocate(Q_NUM);
+	   if (msg == NULL) {
+	   imu_warn("failed to allocate imu msg for disconnect\n");
+	   }
+	   else {
+	   msg->fcode             = IMU_FCODE_DISCONNECT_REQ;
+	   msg->array_context     = 0;
+	   msg->app_context       = 0;
+	   msg->connection_handle = imu_scsi->connection_handle;;
+	   iop_queue_postmsg(Q_NUM, msg);
+	   }
+	 */
+	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] 6+ messages in thread

* Re: [PATCH RFC 0/2] imu-scsi for iop13xx
  2007-01-24 18:22 [PATCH RFC 0/2] imu-scsi for iop13xx dan.j.williams
  2007-01-24 18:24 ` [PATCH RFC 1/2] iop13xx: add base support for the imu Dan Williams
  2007-01-24 18:24 ` [PATCH RFC 2/2] iop13xx: imu scsi driver Dan Williams
@ 2007-01-25 21:15 ` James Bottomley
  2 siblings, 0 replies; 6+ messages in thread
From: James Bottomley @ 2007-01-25 21:15 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux, linux-arm-kernel, linux-scsi, greg.b.tucker

On Wed, 2007-01-24 at 11:22 -0700, dan.j.williams@intel.com wrote:
> The imu (inter-processor messaging unit) on the iop13xx enables mailbox
> style communication between the two cores on iop13xx.  Greg Tucker has
> has wrapped this capability in a scsi driver which allows Linux to
> interface with the other core as if it were a scsi disk target.  I would
> like to submit it for 2.6.21 consideration.  As it is an
> ARM-platform-specific driver does it fall under Russell's purview, or
> James' since it is a scsi driver.

Both of ours, I'm afraid ... as in Russell is the sign off for the arm
pieces and I'll do the SCSI bits.

As to logistics, we could do it split tree, or we could do cross
signoffs for either the SCSI or the ARM tree.

James



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

* Re: [PATCH RFC 2/2] iop13xx: imu scsi driver
  2007-01-24 18:24 ` [PATCH RFC 2/2] iop13xx: imu scsi driver Dan Williams
@ 2007-01-25 21:15   ` James Bottomley
  2007-01-26  7:03     ` Tucker, Greg B
  0 siblings, 1 reply; 6+ messages in thread
From: James Bottomley @ 2007-01-25 21:15 UTC (permalink / raw)
  To: Dan Williams; +Cc: linux, linux-arm-kernel, linux-scsi, greg.b.tucker

There are quite a few commented out debugging statements and deprecated
C++ style comments dotted throughout this driver ... could these be
corrected? (move to C style comments and either implement a debugging
strategy or pull out the statements).

Another thing that strikes me is on the ARM side, it looks like this is
effectively vectoring up to a user space target ... is that true?  And
if so, would the existing in-kernel target infrastructure be a better
way of doing this?

On Wed, 2007-01-24 at 11:24 -0700, Dan Williams wrote:
> +static int imu_queuecommand(struct scsi_cmnd *scp,
[...]
> +	if (!imu_scsi->connection_handle) {
> +		scp->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
> +		done(scp);
> +		return SCSI_MLQUEUE_HOST_BUSY;
> +	}

Absolutely wrong.  Either you execute done() on the command and return
zero, or you *don't* execute done on the command and return a queueing
condition.  Doing both will lead to double completions and really
horrible things.

> +       switch (scp->cmnd[0]) {
> +       case TEST_UNIT_READY:
> +               break;
> +	case READ_10:
> +	case READ_6:
> +	case WRITE_10:
> +	case WRITE_6:
> +	case READ_CAPACITY:
> +	default:

The predicate for this switch looks wrong.  You're not really
interpreting the commands, you appear to be interested in whether they
have data buffers attached.  Might I suggest this might be better done
by

if (scp->sc_data_direction == DMA_NONE)
	break;
else if (scp->use_sg == 0)
	/* request buffer case; hopefully disappearing soon */
else
	/* sg case */



> +		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,
> +						   DMA_BIDIRECTIONAL);
> +			if (buff == 0) {
> +				imu_warn("null return from dma_map_single\n");
> +				msg->flags = IMU_FCODE_STATUS;
> +				msg->status = 0;	//todo: what is error status?
> +				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;
> +
> +			// This shouldn't be necessary
> +			//dma_sync_single(NULL, buff, scp->request_bufflen, DMA_BIDIRECTIONAL);
> +			imu_debug("cmd msg with 1 sgl entry, addr=0x%x\n",
> +				  buff);

Why BIDIRECTIONAL?  This can be expensive on some architectures
(although I don't know about ARM).  However, for these commands, the
scp->sc_data_direction should contain the exact direction you need to
program into the DMA engines. (Consider this the same comment for the
use_sg > 0 portion as well).


> +	iop_queue_postmsg(Q_NUM, msg);

This has error returns ... should you be handling them?


> +static int imu_eh_abort(struct scsi_cmnd *cmd)
> +{
> +	imu_warn("Received eh command abort\n");
> +	//dbg_print_cmd(cmd);
> +	return SUCCESS;
> +}
> +
> +static int imu_eh_device_reset(struct scsi_cmnd *cmd)
> +{
> +	int target = cmd->device->id;
> +	imu_warn("Received eh device reset for target %d\n", target);
> +	return SUCCESS;
> +}
> +
> +static int imu_eh_host_reset(struct scsi_cmnd *cmd)
> +{
> +	imu_warn("Received eh host reset\n");
> +	return SUCCESS;
> +}
> +
> +static int imu_eh_bus_reset(struct scsi_cmnd *cmd)
> +{
> +	imu_warn("Received eh bus reset\n");
> +	return SUCCESS;
> +}

This lot all look really wrong.  All you're doing is printing a message
and then telling the mid-layer the device is recovered, when in fact
you've done nothing to alleviate the error condition.  The mid-layer's
next action will be to send down a TUR to check the device condition,
which will likely fail and continue on to offline the device.

James



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

* RE: [PATCH RFC 2/2] iop13xx: imu scsi driver
  2007-01-25 21:15   ` James Bottomley
@ 2007-01-26  7:03     ` Tucker, Greg B
  0 siblings, 0 replies; 6+ messages in thread
From: Tucker, Greg B @ 2007-01-26  7:03 UTC (permalink / raw)
  To: James Bottomley, Williams, Dan J; +Cc: linux, linux-arm-kernel, linux-scsi

James, thank you for the corrections.  I think I can fix all of these as
suggested and resubmit a new patch shortly.

>Another thing that strikes me is on the ARM side, it looks like this is
>effectively vectoring up to a user space target ... is that true?  And
>if so, would the existing in-kernel target infrastructure be a better
>way of doing this?

No, there's no user space target.  The target is special hardware.  Or
more precisely, special communication hardware to send messages to
off-core hardware.

>> +static int imu_queuecommand(struct scsi_cmnd *scp,
>[...]
>> +	if (!imu_scsi->connection_handle) {
>> +		scp->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
>> +		done(scp);
>> +		return SCSI_MLQUEUE_HOST_BUSY;
>> +	}
>
>Absolutely wrong.  Either you execute done() on the command and return
>zero, or you *don't* execute done on the command and return a queueing
>condition.  Doing both will lead to double completions and really
>horrible things.

I'll return zero like the previous error condition, thanks.

> ...wrong...

Other corrections greatly appreciated and I'll roll into next patch.

--Greg

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

end of thread, other threads:[~2007-01-26  7:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-01-24 18:22 [PATCH RFC 0/2] imu-scsi for iop13xx dan.j.williams
2007-01-24 18:24 ` [PATCH RFC 1/2] iop13xx: add base support for the imu Dan Williams
2007-01-24 18:24 ` [PATCH RFC 2/2] iop13xx: imu scsi driver Dan Williams
2007-01-25 21:15   ` James Bottomley
2007-01-26  7:03     ` Tucker, Greg B
2007-01-25 21:15 ` [PATCH RFC 0/2] imu-scsi for iop13xx James Bottomley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox