linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux
@ 2009-08-03 21:00 ` Greg K-H
  2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
                     ` (6 more replies)
  0 siblings, 7 replies; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:00 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

Hi all,

Here's 5 patches from Martyn Welch that adds VME Bus support to the
kernel.

Martyn took the time and merged the three different existing
implementations for Linux together into something that works, and got
the proper legal agreements from all of the affected parties to release
the code properly.

I'd like to thank him for taking the time to do this work, it is much
appreciated.

These patches all add the code under the drivers/staging/ directory, as
there is still a number of things that needs to be done to clean up the
code to get it into "proper" mergable state, but it's a great first cut.

Thanks again Martyn!

greg k-h

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

* [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
@ 2009-08-03 21:01   ` Greg K-H
  2009-08-08 23:01     ` Emilio G. Cota
  2009-08-10 14:52     ` [PATCH] Staging: vme: fix {master,slave}_get check bug Emilio G. Cota
  2009-08-03 21:01   ` [patch 2/5] Staging: vme: add VME userspace driver Greg K-H
                     ` (5 subsequent siblings)
  6 siblings, 2 replies; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:01 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

[-- Attachment #1: staging-vme-framework-for-the-linux-kernel.patch --]
[-- Type: text/plain, Size: 46197 bytes --]

From: Martyn Welch <martyn.welch@gefanuc.com>

This framework aims to colelese, extend and improve the VME Linux
drivers found at vmelinux.org, universe2.sourceforge.net and
openfmi.net/frs/?group_id=144. The last 2 drivers appear to be forks of
the original code found at vmelinux.org though have extended the
codebase.

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/staging/Kconfig          |    2 
 drivers/staging/Makefile         |    1 
 drivers/staging/vme/Kconfig      |   17 
 drivers/staging/vme/Makefile     |    7 
 drivers/staging/vme/vme.c        | 1371 +++++++++++++++++++++++++++++++++++++++
 drivers/staging/vme/vme.h        |  153 ++++
 drivers/staging/vme/vme_bridge.h |  249 +++++++
 7 files changed, 1800 insertions(+)

--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -139,5 +139,7 @@ source "drivers/staging/udlfb/Kconfig"
 
 source "drivers/staging/hv/Kconfig"
 
+source "drivers/staging/vme/Kconfig"
+
 endif # !STAGING_EXCLUDE_BUILD
 endif # STAGING
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_FB_UDL)		+= udlfb/
 obj-$(CONFIG_MSM_ADSP)		+= dream/qdsp5/ dream/smd/
 obj-$(CONFIG_MSM_CAMERA)	+= dream/camera/
 obj-$(CONFIG_HYPERV)		+= hv/
+obj-$(CONFIG_VME)		+= vme/
--- /dev/null
+++ b/drivers/staging/vme/Kconfig
@@ -0,0 +1,17 @@
+#
+# VME configuration.
+#
+
+menuconfig VME
+	tristate "VME bridge support"
+	depends on PCI
+	---help---
+	  If you say Y here you get support for the VME bridge Framework.
+
+if VME
+
+#source "drivers/staging/vme/bridges/Kconfig"
+#
+#source "drivers/staging/vme/devices/Kconfig"
+
+endif # VME
--- /dev/null
+++ b/drivers/staging/vme/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the VME bridge device drivers.
+#
+obj-$(CONFIG_VME)		+= vme.o
+
+#obj-y				+= bridges/
+#obj-y				+= devices/
--- /dev/null
+++ b/drivers/staging/vme/vme_bridge.h
@@ -0,0 +1,249 @@
+#ifndef _VME_BRIDGE_H_
+#define _VME_BRIDGE_H_
+
+#define VME_CRCSR_BUF_SIZE (508*1024)
+#define VME_SLOTS_MAX 32
+/*
+ * Resource structures
+ */
+struct vme_master_resource {
+	struct list_head list;
+	struct vme_bridge *parent;
+	/*
+	 * We are likely to need to access the VME bus in interrupt context, so
+	 * protect master routines with a spinlock rather than a semaphore.
+	 */
+	spinlock_t lock;
+	int locked;
+	int number;
+	vme_address_t address_attr;
+	vme_cycle_t cycle_attr;
+	vme_width_t width_attr;
+	struct resource pci_resource;	/* XXX Rename to be bus agnostic */
+	void *kern_base;
+};
+
+struct vme_slave_resource {
+	struct list_head list;
+	struct vme_bridge *parent;
+	struct semaphore sem;
+	int locked;
+	int number;
+	vme_address_t address_attr;
+	vme_cycle_t cycle_attr;
+};
+
+struct vme_dma_pattern {
+	u32 pattern;
+	vme_pattern_t type;
+};
+
+struct vme_dma_pci {
+	dma_addr_t address;
+};
+
+struct vme_dma_vme {
+	unsigned long long address;
+	vme_address_t aspace;
+	vme_cycle_t cycle;
+	vme_width_t dwidth;
+};
+
+struct vme_dma_list {
+	struct list_head list;
+	struct vme_dma_resource *parent;
+	struct list_head entries;
+	struct semaphore sem;
+};
+
+struct vme_dma_resource {
+	struct list_head list;
+	struct vme_bridge *parent;
+	struct semaphore sem;
+	int locked;
+	int number;
+	struct list_head pending;
+	struct list_head running;
+};
+
+struct vme_bus_error {
+	struct list_head list;
+	unsigned long long address;
+	u32 attributes;
+};
+
+struct vme_callback {
+	void (*func)(int, int, void*);
+	void *priv_data;
+};
+
+struct vme_irq {
+	int count;
+	struct vme_callback callback[255];
+};
+
+/* Allow 16 characters for name (including null character) */
+#define VMENAMSIZ 16
+
+/* This structure stores all the information about one bridge
+ * The structure should be dynamically allocated by the driver and one instance
+ * of the structure should be present for each VME chip present in the system.
+ *
+ * Currently we assume that all chips are PCI-based
+ */
+struct vme_bridge {
+        char name[VMENAMSIZ];
+	int num;
+	struct list_head master_resources;
+	struct list_head slave_resources;
+	struct list_head dma_resources;
+
+	struct list_head vme_errors;	/* List for errors generated on VME */
+
+	/* Bridge Info - XXX Move to private structure? */
+	struct device *parent;	/* Generic device struct (pdev->dev for PCI) */
+	void * base;		/* Base Address of device registers */
+
+	struct device dev[VME_SLOTS_MAX];	/* Device registered with
+						 * device model on VME bus
+						 */
+
+	/* Interrupt callbacks */
+	struct vme_irq irq[7];
+
+	/* Slave Functions */
+	int (*slave_get) (struct vme_slave_resource *, int *,
+		unsigned long long *, unsigned long long *, dma_addr_t *,
+		vme_address_t *, vme_cycle_t *);
+	int (*slave_set) (struct vme_slave_resource *, int, unsigned long long,
+		unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
+
+	/* Master Functions */
+	int (*master_get) (struct vme_master_resource *, int *,
+		unsigned long long *, unsigned long long *, vme_address_t *,
+		vme_cycle_t *, vme_width_t *);
+	int (*master_set) (struct vme_master_resource *, int,
+		unsigned long long, unsigned long long,  vme_address_t,
+		vme_cycle_t, vme_width_t);
+	ssize_t (*master_read) (struct vme_master_resource *, void *, size_t,
+		loff_t);
+	ssize_t (*master_write) (struct vme_master_resource *, void *, size_t,
+		loff_t);
+	unsigned int (*master_rmw) (struct vme_master_resource *, unsigned int,
+		unsigned int, unsigned int, loff_t);
+
+	/* DMA Functions */
+	int (*dma_list_add) (struct vme_dma_list *, struct vme_dma_attr *,
+		struct vme_dma_attr *, size_t);
+	int (*dma_list_exec) (struct vme_dma_list *);
+	int (*dma_list_empty) (struct vme_dma_list *);
+
+	/* Interrupt Functions */
+	int (*request_irq) (int, int, void (*cback)(int, int, void*), void *);
+	void (*free_irq) (int, int);
+	int (*generate_irq) (int, int);
+
+	/* Location monitor functions */
+	int (*lm_set) (unsigned long long, vme_address_t, vme_cycle_t);
+	int (*lm_get) (unsigned long long *, vme_address_t *, vme_cycle_t *);
+	int (*lm_attach) (int, void (*callback)(int));
+	int (*lm_detach) (int);
+
+	/* CR/CSR space functions */
+	int (*slot_get) (void);
+	/* Use standard master read and write functions to access CR/CSR */
+
+#if 0
+	int (*set_prefetch) (void);
+	int (*get_prefetch) (void);
+	int (*set_arbiter) (void);
+	int (*get_arbiter) (void);
+	int (*set_requestor) (void);
+	int (*get_requestor) (void);
+#endif
+};
+
+int vme_register_bridge (struct vme_bridge *);
+void vme_unregister_bridge (struct vme_bridge *);
+
+#endif /* _VME_BRIDGE_H_ */
+
+#if 0
+/*
+ *  VMEbus GET INFO Arg Structure
+ */
+struct vmeInfoCfg {
+	int vmeSlotNum;		/*  VME slot number of interest */
+	int boardResponded;	/* Board responded */
+	char sysConFlag;	/*  System controller flag */
+	int vmeControllerID;	/*  Vendor/device ID of VME bridge */
+	int vmeControllerRev;	/*  Revision of VME bridge */
+	char osName[8];		/*  Name of OS e.g. "Linux" */
+	int vmeSharedDataValid;	/*  Validity of data struct */
+	int vmeDriverRev;	/*  Revision of VME driver */
+	unsigned int vmeAddrHi[8];	/* Address on VME bus */
+	unsigned int vmeAddrLo[8];	/* Address on VME bus */
+	unsigned int vmeSize[8];	/* Size on VME bus */
+	unsigned int vmeAm[8];	/* Address modifier on VME bus */
+	int reserved;		/* For future use */
+};
+typedef struct vmeInfoCfg vmeInfoCfg_t;
+
+/*
+ *  VMEbus Requester Arg Structure
+ */
+struct vmeRequesterCfg {
+	int requestLevel;	/*  Requester Bus Request Level */
+	char fairMode;		/*  Requester Fairness Mode Indicator */
+	int releaseMode;	/*  Requester Bus Release Mode */
+	int timeonTimeoutTimer;	/*  Master Time-on Time-out Timer */
+	int timeoffTimeoutTimer;	/*  Master Time-off Time-out Timer */
+	int reserved;		/* For future use */
+};
+typedef struct vmeRequesterCfg vmeRequesterCfg_t;
+
+/*
+ *  VMEbus Arbiter Arg Structure
+ */
+struct vmeArbiterCfg {
+	vme_arbitration_t arbiterMode;	/*  Arbitration Scheduling Algorithm */
+	char arbiterTimeoutFlag;	/*  Arbiter Time-out Timer Indicator */
+	int globalTimeoutTimer;	/*  VMEbus Global Time-out Timer */
+	char noEarlyReleaseFlag;	/*  No Early Release on BBUSY */
+	int reserved;		/* For future use */
+};
+typedef struct vmeArbiterCfg vmeArbiterCfg_t;
+
+
+/*
+ *  VMEbus RMW Configuration Data
+ */
+struct vmeRmwCfg {
+	unsigned int targetAddrU;	/*  VME Address (Upper) to trigger RMW cycle */
+	unsigned int targetAddr;	/*  VME Address (Lower) to trigger RMW cycle */
+	vme_address_t addrSpace;	/*  VME Address Space */
+	int enableMask;		/*  Bit mask defining the bits of interest */
+	int compareData;	/*  Data to be compared with the data read */
+	int swapData;		/*  Data written to the VMEbus on success */
+	int maxAttempts;	/*  Maximum times to try */
+	int numAttempts;	/*  Number of attempts before success */
+	int reserved;		/* For future use */
+
+};
+typedef struct vmeRmwCfg vmeRmwCfg_t;
+
+/*
+ *  VMEbus Location Monitor Arg Structure
+ */
+struct vmeLmCfg {
+	unsigned int addrU;	/*  Location Monitor Address upper */
+	unsigned int addr;	/*  Location Monitor Address lower */
+	vme_address_t addrSpace;	/*  Address Space */
+	int userAccessType;	/*  User/Supervisor Access Type */
+	int dataAccessType;	/*  Data/Program Access Type */
+	int lmWait;		/* Time to wait for access */
+	int lmEvents;		/* Lm event mask */
+	int reserved;		/* For future use */
+};
+typedef struct vmeLmCfg vmeLmCfg_t;
+#endif
--- /dev/null
+++ b/drivers/staging/vme/vme.c
@@ -0,0 +1,1371 @@
+/*
+ * VME Bridge Framework
+ *
+ * Author: Martyn Welch <martyn.welch@gefanuc.com>
+ * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by Tom Armistead and Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/pagemap.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/syscalls.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include "vme.h"
+#include "vme_bridge.h"
+
+/* Bitmask and semaphore to keep track of bridge numbers */
+static unsigned int vme_bus_numbers;
+DECLARE_MUTEX(vme_bus_num_sem);
+
+static void __exit vme_exit (void);
+static int __init vme_init (void);
+
+
+/*
+ * Find the bridge resource associated with a specific device resource
+ */
+static struct vme_bridge *dev_to_bridge(struct device *dev)
+{
+	return dev->platform_data;
+}
+
+/*
+ * Find the bridge that the resource is associated with.
+ */
+static struct vme_bridge *find_bridge(struct vme_resource *resource)
+{
+	/* Get list to search */
+	switch (resource->type) {
+	case VME_MASTER:
+		return list_entry(resource->entry, struct vme_master_resource,
+			list)->parent;
+		break;
+	case VME_SLAVE:
+		return list_entry(resource->entry, struct vme_slave_resource,
+			list)->parent;
+		break;
+	case VME_DMA:
+		return list_entry(resource->entry, struct vme_dma_resource,
+			list)->parent;
+		break;
+	default:
+		printk(KERN_ERR "Unknown resource type\n");
+		return NULL;
+		break;
+	}
+}
+
+/*
+ * Allocate a contiguous block of memory for use by the driver. This is used to
+ * create the buffers for the slave windows.
+ *
+ * XXX VME bridges could be available on buses other than PCI. At the momment
+ *     this framework only supports PCI devices.
+ */
+void * vme_alloc_consistent(struct vme_resource *resource, size_t size,
+	dma_addr_t *dma)
+{
+	struct vme_bridge *bridge;
+	struct pci_dev *pdev;
+
+	if(resource == NULL) {
+		printk("No resource\n");
+		return NULL;
+	}
+
+	bridge = find_bridge(resource);
+	if(bridge == NULL) {
+		printk("Can't find bridge\n");
+		return NULL;
+	}
+
+	/* Find pci_dev container of dev */
+	if (bridge->parent == NULL) {
+		printk("Dev entry NULL\n");
+		return NULL;
+	}
+	pdev = container_of(bridge->parent, struct pci_dev, dev);
+
+	return pci_alloc_consistent(pdev, size, dma);
+}
+EXPORT_SYMBOL(vme_alloc_consistent);
+
+/*
+ * Free previously allocated contiguous block of memory.
+ *
+ * XXX VME bridges could be available on buses other than PCI. At the momment
+ *     this framework only supports PCI devices.
+ */
+void vme_free_consistent(struct vme_resource *resource, size_t size,
+	void *vaddr, dma_addr_t dma)
+{
+	struct vme_bridge *bridge;
+	struct pci_dev *pdev;
+
+	if(resource == NULL) {
+		printk("No resource\n");
+		return;
+	}
+
+	bridge = find_bridge(resource);
+	if(bridge == NULL) {
+		printk("Can't find bridge\n");
+		return;
+	}
+
+	/* Find pci_dev container of dev */
+	pdev = container_of(bridge->parent, struct pci_dev, dev);
+
+	pci_free_consistent(pdev, size, vaddr, dma);
+}
+EXPORT_SYMBOL(vme_free_consistent);
+
+size_t vme_get_size(struct vme_resource *resource)
+{
+	int enabled, retval;
+	unsigned long long base, size;
+	dma_addr_t buf_base;
+	vme_address_t aspace;
+	vme_cycle_t cycle;
+	vme_width_t dwidth;
+
+	switch (resource->type) {
+	case VME_MASTER:
+		retval = vme_master_get(resource, &enabled, &base, &size,
+			&aspace, &cycle, &dwidth);
+
+		return size;
+		break;
+	case VME_SLAVE:
+		retval = vme_slave_get(resource, &enabled, &base, &size,
+			&buf_base, &aspace, &cycle);
+
+		return size;
+		break;
+	case VME_DMA:
+		return 0;
+		break;
+	default:
+		printk(KERN_ERR "Unknown resource type\n");
+		return 0;
+		break;
+	}
+}
+EXPORT_SYMBOL(vme_get_size);
+
+static int vme_check_window(vme_address_t aspace, unsigned long long vme_base,
+	unsigned long long size)
+{
+	int retval = 0;
+
+	switch (aspace) {
+	case VME_A16:
+		if (((vme_base + size) > VME_A16_MAX) ||
+				(vme_base > VME_A16_MAX))
+			retval = -EFAULT;
+		break;
+	case VME_A24:
+		if (((vme_base + size) > VME_A24_MAX) ||
+				(vme_base > VME_A24_MAX))
+			retval = -EFAULT;
+		break;
+	case VME_A32:
+		if (((vme_base + size) > VME_A32_MAX) ||
+				(vme_base > VME_A32_MAX))
+			retval = -EFAULT;
+		break;
+	case VME_A64:
+		/*
+		 * Any value held in an unsigned long long can be used as the
+		 * base
+		 */
+		break;
+	case VME_CRCSR:
+		if (((vme_base + size) > VME_CRCSR_MAX) ||
+				(vme_base > VME_CRCSR_MAX))
+			retval = -EFAULT;
+		break;
+	case VME_USER1:
+	case VME_USER2:
+	case VME_USER3:
+	case VME_USER4:
+		/* User Defined */
+		break;
+	default:
+		printk("Invalid address space\n");
+		retval = -EINVAL;
+		break;
+	}
+
+	return retval;
+}
+
+/*
+ * Request a slave image with specific attributes, return some unique
+ * identifier.
+ */
+struct vme_resource * vme_slave_request(struct device *dev,
+	vme_address_t address, vme_cycle_t cycle)
+{
+	struct vme_bridge *bridge;
+	struct list_head *slave_pos = NULL;
+	struct vme_slave_resource *allocated_image = NULL;
+	struct vme_slave_resource *slave_image = NULL;
+	struct vme_resource *resource = NULL;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		goto err_bus;
+	}
+
+	/* Loop through slave resources */
+	list_for_each(slave_pos, &(bridge->slave_resources)) {
+		slave_image = list_entry(slave_pos,
+			struct vme_slave_resource, list);
+
+		if (slave_image == NULL) {
+			printk("Registered NULL Slave resource\n");
+			continue;
+		}
+
+		/* Find an unlocked and compatible image */
+		down(&(slave_image->sem));
+		if(((slave_image->address_attr & address) == address) &&
+			((slave_image->cycle_attr & cycle) == cycle) &&
+			(slave_image->locked == 0)) {
+
+			slave_image->locked = 1;
+			up(&(slave_image->sem));
+			allocated_image = slave_image;
+			break;
+		}
+		up(&(slave_image->sem));
+	}
+
+	/* No free image */
+	if (allocated_image == NULL)
+		goto err_image;
+
+	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
+	if (resource == NULL) {
+		printk(KERN_WARNING "Unable to allocate resource structure\n");
+		goto err_alloc;
+	}
+	resource->type = VME_SLAVE;
+	resource->entry = &(allocated_image->list);
+
+	return resource;
+
+err_alloc:
+	/* Unlock image */
+	down(&(slave_image->sem));
+	slave_image->locked = 0;
+	up(&(slave_image->sem));
+err_image:
+err_bus:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_slave_request);
+
+int vme_slave_set (struct vme_resource *resource, int enabled,
+	unsigned long long vme_base, unsigned long long size,
+	dma_addr_t buf_base, vme_address_t aspace, vme_cycle_t cycle)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_slave_resource *image;
+	int retval;
+
+	if (resource->type != VME_SLAVE) {
+		printk("Not a slave resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_slave_resource, list);
+
+	if (bridge->slave_set == NULL) {
+		printk("Function not supported\n");
+		return -ENOSYS;
+	}
+
+	if(!(((image->address_attr & aspace) == aspace) &&
+		((image->cycle_attr & cycle) == cycle))) {
+		printk("Invalid attributes\n");
+		return -EINVAL;
+	}
+
+	retval = vme_check_window(aspace, vme_base, size);
+	if(retval)
+		return retval;
+
+	return bridge->slave_set(image, enabled, vme_base, size, buf_base,
+		aspace, cycle);
+}
+EXPORT_SYMBOL(vme_slave_set);
+
+int vme_slave_get (struct vme_resource *resource, int *enabled,
+	unsigned long long *vme_base, unsigned long long *size,
+	dma_addr_t *buf_base, vme_address_t *aspace, vme_cycle_t *cycle)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_slave_resource *image;
+
+	if (resource->type != VME_SLAVE) {
+		printk("Not a slave resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_slave_resource, list);
+
+	if (bridge->slave_set == NULL) {
+		printk("vme_slave_get not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->slave_get(image, enabled, vme_base, size, buf_base,
+		aspace, cycle);
+}
+EXPORT_SYMBOL(vme_slave_get);
+
+void vme_slave_free(struct vme_resource *resource)
+{
+	struct vme_slave_resource *slave_image;
+
+	if (resource->type != VME_SLAVE) {
+		printk("Not a slave resource\n");
+		return;
+	}
+
+	slave_image = list_entry(resource->entry, struct vme_slave_resource,
+		list);
+	if (slave_image == NULL) {
+		printk("Can't find slave resource\n");
+		return;
+	}
+
+	/* Unlock image */
+	down(&(slave_image->sem));
+	if (slave_image->locked == 0)
+		printk(KERN_ERR "Image is already free\n");
+
+	slave_image->locked = 0;
+	up(&(slave_image->sem));
+
+	/* Free up resource memory */
+	kfree(resource);
+}
+EXPORT_SYMBOL(vme_slave_free);
+
+/*
+ * Request a master image with specific attributes, return some unique
+ * identifier.
+ */
+struct vme_resource * vme_master_request(struct device *dev,
+	vme_address_t address, vme_cycle_t cycle, vme_width_t dwidth)
+{
+	struct vme_bridge *bridge;
+	struct list_head *master_pos = NULL;
+	struct vme_master_resource *allocated_image = NULL;
+	struct vme_master_resource *master_image = NULL;
+	struct vme_resource *resource = NULL;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		goto err_bus;
+	}
+
+	/* Loop through master resources */
+	list_for_each(master_pos, &(bridge->master_resources)) {
+		master_image = list_entry(master_pos,
+			struct vme_master_resource, list);
+
+		if (master_image == NULL) {
+			printk(KERN_WARNING "Registered NULL master resource\n");
+			continue;
+		}
+
+		/* Find an unlocked and compatible image */
+		spin_lock(&(master_image->lock));
+		if(((master_image->address_attr & address) == address) &&
+			((master_image->cycle_attr & cycle) == cycle) &&
+			((master_image->width_attr & dwidth) == dwidth) &&
+			(master_image->locked == 0)) {
+
+			master_image->locked = 1;
+			spin_unlock(&(master_image->lock));
+			allocated_image = master_image;
+			break;
+		}
+		spin_unlock(&(master_image->lock));
+	}
+
+	/* Check to see if we found a resource */
+	if (allocated_image == NULL) {
+		printk(KERN_ERR "Can't find a suitable resource\n");
+		goto err_image;
+	}
+
+	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
+	if (resource == NULL) {
+		printk(KERN_ERR "Unable to allocate resource structure\n");
+		goto err_alloc;
+	}
+	resource->type = VME_MASTER;
+	resource->entry = &(allocated_image->list);
+
+	return resource;
+
+	kfree(resource);
+err_alloc:
+	/* Unlock image */
+	spin_lock(&(master_image->lock));
+	master_image->locked = 0;
+	spin_unlock(&(master_image->lock));
+err_image:
+err_bus:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_master_request);
+
+int vme_master_set (struct vme_resource *resource, int enabled,
+	unsigned long long vme_base, unsigned long long size,
+	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_master_resource *image;
+	int retval;
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_master_resource, list);
+
+	if (bridge->master_set == NULL) {
+		printk("vme_master_set not supported\n");
+		return -EINVAL;
+	}
+
+	if(!(((image->address_attr & aspace) == aspace) &&
+		((image->cycle_attr & cycle) == cycle) &&
+		((image->width_attr & dwidth) == dwidth))) {
+		printk("Invalid attributes\n");
+		return -EINVAL;
+	}
+
+	retval = vme_check_window(aspace, vme_base, size);
+	if(retval)
+		return retval;
+
+	return bridge->master_set(image, enabled, vme_base, size, aspace,
+		cycle, dwidth);
+}
+EXPORT_SYMBOL(vme_master_set);
+
+int vme_master_get (struct vme_resource *resource, int *enabled,
+	unsigned long long *vme_base, unsigned long long *size,
+	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_master_resource *image;
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_master_resource, list);
+
+	if (bridge->master_set == NULL) {
+		printk("vme_master_set not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->master_get(image, enabled, vme_base, size, aspace,
+		cycle, dwidth);
+}
+EXPORT_SYMBOL(vme_master_get);
+
+/*
+ * Read data out of VME space into a buffer.
+ */
+ssize_t vme_master_read (struct vme_resource *resource, void *buf, size_t count,
+	loff_t offset)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_master_resource *image;
+	size_t length;
+
+	if (bridge->master_read == NULL) {
+		printk("Reading from resource not supported\n");
+		return -EINVAL;
+	}
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_master_resource, list);
+
+	length = vme_get_size(resource);
+
+	if (offset > length) {
+		printk("Invalid Offset\n");
+		return -EFAULT;
+	}
+
+	if ((offset + count) > length)
+		count = length - offset;
+
+	return bridge->master_read(image, buf, count, offset);
+
+}
+EXPORT_SYMBOL(vme_master_read);
+
+/*
+ * Write data out to VME space from a buffer.
+ */
+ssize_t vme_master_write (struct vme_resource *resource, void *buf,
+	size_t count, loff_t offset)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_master_resource *image;
+	size_t length;
+
+	if (bridge->master_write == NULL) {
+		printk("Writing to resource not supported\n");
+		return -EINVAL;
+	}
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_master_resource, list);
+
+	length = vme_get_size(resource);
+
+	if (offset > length) {
+		printk("Invalid Offset\n");
+		return -EFAULT;
+	}
+
+	if ((offset + count) > length)
+		count = length - offset;
+
+	return bridge->master_write(image, buf, count, offset);
+}
+EXPORT_SYMBOL(vme_master_write);
+
+/*
+ * Perform RMW cycle to provided location.
+ */
+unsigned int vme_master_rmw (struct vme_resource *resource, unsigned int mask,
+	unsigned int compare, unsigned int swap, loff_t offset)
+{
+	struct vme_bridge *bridge = find_bridge(resource);
+	struct vme_master_resource *image;
+
+	if (bridge->master_rmw == NULL) {
+		printk("Writing to resource not supported\n");
+		return -EINVAL;
+	}
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return -EINVAL;
+	}
+
+	image = list_entry(resource->entry, struct vme_master_resource, list);
+
+	return bridge->master_rmw(image, mask, compare, swap, offset);
+}
+EXPORT_SYMBOL(vme_master_rmw);
+
+void vme_master_free(struct vme_resource *resource)
+{
+	struct vme_master_resource *master_image;
+
+	if (resource->type != VME_MASTER) {
+		printk("Not a master resource\n");
+		return;
+	}
+
+	master_image = list_entry(resource->entry, struct vme_master_resource,
+		list);
+	if (master_image == NULL) {
+		printk("Can't find master resource\n");
+		return;
+	}
+
+	/* Unlock image */
+	spin_lock(&(master_image->lock));
+	if (master_image->locked == 0)
+		printk(KERN_ERR "Image is already free\n");
+
+	master_image->locked = 0;
+	spin_unlock(&(master_image->lock));
+
+	/* Free up resource memory */
+	kfree(resource);
+}
+EXPORT_SYMBOL(vme_master_free);
+
+/*
+ * Request a DMA controller with specific attributes, return some unique
+ * identifier.
+ */
+struct vme_resource *vme_request_dma(struct device *dev)
+{
+	struct vme_bridge *bridge;
+	struct list_head *dma_pos = NULL;
+	struct vme_dma_resource *allocated_ctrlr = NULL;
+	struct vme_dma_resource *dma_ctrlr = NULL;
+	struct vme_resource *resource = NULL;
+
+	/* XXX Not checking resource attributes */
+	printk(KERN_ERR "No VME resource Attribute tests done\n");
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		goto err_bus;
+	}
+
+	/* Loop through DMA resources */
+	list_for_each(dma_pos, &(bridge->dma_resources)) {
+		dma_ctrlr = list_entry(dma_pos,
+			struct vme_dma_resource, list);
+
+		if (dma_ctrlr == NULL) {
+			printk("Registered NULL DMA resource\n");
+			continue;
+		}
+
+		/* Find an unlocked controller */
+		down(&(dma_ctrlr->sem));
+		if(dma_ctrlr->locked == 0) {
+			dma_ctrlr->locked = 1;
+			up(&(dma_ctrlr->sem));
+			allocated_ctrlr = dma_ctrlr;
+			break;
+		}
+		up(&(dma_ctrlr->sem));
+	}
+
+	/* Check to see if we found a resource */
+	if (allocated_ctrlr == NULL)
+		goto err_ctrlr;
+
+	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
+	if (resource == NULL) {
+		printk(KERN_WARNING "Unable to allocate resource structure\n");
+		goto err_alloc;
+	}
+	resource->type = VME_DMA;
+	resource->entry = &(allocated_ctrlr->list);
+
+	return resource;
+
+err_alloc:
+	/* Unlock image */
+	down(&(dma_ctrlr->sem));
+	dma_ctrlr->locked = 0;
+	up(&(dma_ctrlr->sem));
+err_ctrlr:
+err_bus:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_request_dma);
+
+/*
+ * Start new list
+ */
+struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
+{
+	struct vme_dma_resource *ctrlr;
+	struct vme_dma_list *dma_list;
+
+	if (resource->type != VME_DMA) {
+		printk("Not a DMA resource\n");
+		return NULL;
+	}
+
+	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
+
+	dma_list = (struct vme_dma_list *)kmalloc(
+		sizeof(struct vme_dma_list), GFP_KERNEL);
+	if(dma_list == NULL) {
+		printk("Unable to allocate memory for new dma list\n");
+		return NULL;
+	}
+	INIT_LIST_HEAD(&(dma_list->entries));
+	dma_list->parent = ctrlr;
+	init_MUTEX(&(dma_list->sem));
+
+	return dma_list;
+}
+EXPORT_SYMBOL(vme_new_dma_list);
+
+/*
+ * Create "Pattern" type attributes
+ */
+struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern,
+	vme_pattern_t type)
+{
+	struct vme_dma_attr *attributes;
+	struct vme_dma_pattern *pattern_attr;
+
+	attributes = (struct vme_dma_attr *)kmalloc(
+		sizeof(struct vme_dma_attr), GFP_KERNEL);
+	if(attributes == NULL) {
+		printk("Unable to allocate memory for attributes structure\n");
+		goto err_attr;
+	}
+
+	pattern_attr = (struct vme_dma_pattern *)kmalloc(
+		sizeof(struct vme_dma_pattern), GFP_KERNEL);
+	if(pattern_attr == NULL) {
+		printk("Unable to allocate memory for pattern attributes\n");
+		goto err_pat;
+	}
+
+	attributes->type = VME_DMA_PATTERN;
+	attributes->private = (void *)pattern_attr;
+
+	pattern_attr->pattern = pattern;
+	pattern_attr->type = type;
+
+	return attributes;
+
+	kfree(pattern_attr);
+err_pat:
+	kfree(attributes);
+err_attr:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_dma_pattern_attribute);
+
+/*
+ * Create "PCI" type attributes
+ */
+struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
+{
+	struct vme_dma_attr *attributes;
+	struct vme_dma_pci *pci_attr;
+
+	/* XXX Run some sanity checks here */
+
+	attributes = (struct vme_dma_attr *)kmalloc(
+		sizeof(struct vme_dma_attr), GFP_KERNEL);
+	if(attributes == NULL) {
+		printk("Unable to allocate memory for attributes structure\n");
+		goto err_attr;
+	}
+
+	pci_attr = (struct vme_dma_pci *)kmalloc(sizeof(struct vme_dma_pci),
+		GFP_KERNEL);
+	if(pci_attr == NULL) {
+		printk("Unable to allocate memory for pci attributes\n");
+		goto err_pci;
+	}
+
+
+
+	attributes->type = VME_DMA_PCI;
+	attributes->private = (void *)pci_attr;
+
+	pci_attr->address = address;
+
+	return attributes;
+
+	kfree(pci_attr);
+err_pci:
+	kfree(attributes);
+err_attr:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_dma_pci_attribute);
+
+/*
+ * Create "VME" type attributes
+ */
+struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
+	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
+{
+	struct vme_dma_attr *attributes;
+	struct vme_dma_vme *vme_attr;
+
+	/* XXX Run some sanity checks here */
+
+	attributes = (struct vme_dma_attr *)kmalloc(
+		sizeof(struct vme_dma_attr), GFP_KERNEL);
+	if(attributes == NULL) {
+		printk("Unable to allocate memory for attributes structure\n");
+		goto err_attr;
+	}
+
+	vme_attr = (struct vme_dma_vme *)kmalloc(sizeof(struct vme_dma_vme),
+		GFP_KERNEL);
+	if(vme_attr == NULL) {
+		printk("Unable to allocate memory for vme attributes\n");
+		goto err_vme;
+	}
+
+	attributes->type = VME_DMA_VME;
+	attributes->private = (void *)vme_attr;
+
+	vme_attr->address = address;
+	vme_attr->aspace = aspace;
+	vme_attr->cycle = cycle;
+	vme_attr->dwidth = dwidth;
+
+	return attributes;
+
+	kfree(vme_attr);
+err_vme:
+	kfree(attributes);
+err_attr:
+	return NULL;
+}
+EXPORT_SYMBOL(vme_dma_vme_attribute);
+
+/*
+ * Free attribute
+ */
+void vme_dma_free_attribute(struct vme_dma_attr *attributes)
+{
+	kfree(attributes->private);
+	kfree(attributes);
+}
+EXPORT_SYMBOL(vme_dma_free_attribute);
+
+int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
+	struct vme_dma_attr *dest, size_t count)
+{
+	struct vme_bridge *bridge = list->parent->parent;
+	int retval;
+
+	if (bridge->dma_list_add == NULL) {
+		printk("Link List DMA generation not supported\n");
+		return -EINVAL;
+	}
+
+	if (down_trylock(&(list->sem))) {
+		printk("Link List already submitted\n");
+		return -EINVAL;
+	}
+
+	retval = bridge->dma_list_add(list, src, dest, count);
+
+	up(&(list->sem));
+
+	return retval;
+}
+EXPORT_SYMBOL(vme_dma_list_add);
+
+int vme_dma_list_exec(struct vme_dma_list *list)
+{
+	struct vme_bridge *bridge = list->parent->parent;
+	int retval;
+
+	if (bridge->dma_list_exec == NULL) {
+		printk("Link List DMA execution not supported\n");
+		return -EINVAL;
+	}
+
+	down(&(list->sem));
+
+	retval = bridge->dma_list_exec(list);
+
+	up(&(list->sem));
+
+	return retval;
+}
+EXPORT_SYMBOL(vme_dma_list_exec);
+
+int vme_dma_list_free(struct vme_dma_list *list)
+{
+	struct vme_bridge *bridge = list->parent->parent;
+	int retval;
+
+	if (bridge->dma_list_empty == NULL) {
+		printk("Emptying of Link Lists not supported\n");
+		return -EINVAL;
+	}
+
+	if (down_trylock(&(list->sem))) {
+		printk("Link List in use\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Empty out all of the entries from the dma list. We need to go to the
+	 * low level driver as dma entries are driver specific.
+	 */
+	retval = bridge->dma_list_empty(list);
+	if (retval) {
+		printk("Unable to empty link-list entries\n");
+		up(&(list->sem));
+		return retval;
+	}
+	up(&(list->sem));
+	kfree(list);
+
+	return retval;
+}
+EXPORT_SYMBOL(vme_dma_list_free);
+
+int vme_dma_free(struct vme_resource *resource)
+{
+	struct vme_dma_resource *ctrlr;
+
+	if (resource->type != VME_DMA) {
+		printk("Not a DMA resource\n");
+		return -EINVAL;
+	}
+
+	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
+
+	if (down_trylock(&(ctrlr->sem))) {
+		printk("Resource busy, can't free\n");
+		return -EBUSY;
+	}
+
+	if (!(list_empty(&(ctrlr->pending)) && list_empty(&(ctrlr->running)))) {
+		printk("Resource still processing transfers\n");
+		up(&(ctrlr->sem));
+		return -EBUSY;
+	}
+
+	ctrlr->locked = 0;
+
+	up(&(ctrlr->sem));
+
+	return 0;
+}
+EXPORT_SYMBOL(vme_dma_free);
+
+int vme_request_irq(struct device *dev, int level, int statid,
+	void (*callback)(int level, int vector, void *priv_data),
+	void *priv_data)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if((level < 1) || (level > 7)) {
+		printk(KERN_WARNING "Invalid interrupt level\n");
+		return -EINVAL;
+	}
+
+	if (bridge->request_irq == NULL) {
+		printk("Registering interrupts not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->request_irq(level, statid, callback, priv_data);
+}
+EXPORT_SYMBOL(vme_request_irq);
+
+void vme_free_irq(struct device *dev, int level, int statid)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return;
+	}
+
+	if((level < 1) || (level > 7)) {
+		printk(KERN_WARNING "Invalid interrupt level\n");
+		return;
+	}
+
+	if (bridge->free_irq == NULL) {
+		printk("Freeing interrupts not supported\n");
+		return;
+	}
+
+	bridge->free_irq(level, statid);
+}
+EXPORT_SYMBOL(vme_free_irq);
+
+int vme_generate_irq(struct device *dev, int level, int statid)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if((level < 1) || (level > 7)) {
+		printk(KERN_WARNING "Invalid interrupt level\n");
+		return -EINVAL;
+	}
+
+	if (bridge->generate_irq == NULL) {
+		printk("Interrupt generation not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->generate_irq(level, statid);
+}
+EXPORT_SYMBOL(vme_generate_irq);
+
+int vme_lm_set(struct device *dev, unsigned long long lm_base, vme_address_t aspace,
+	vme_cycle_t cycle)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if (bridge->lm_set == NULL) {
+		printk("vme_lm_set not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->lm_set(lm_base, aspace, cycle);
+}
+EXPORT_SYMBOL(vme_lm_set);
+
+int vme_lm_get(struct device *dev, unsigned long long *lm_base, vme_address_t *aspace,
+	vme_cycle_t *cycle)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if (bridge->lm_get == NULL) {
+		printk("vme_lm_get not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->lm_get(lm_base, aspace, cycle);
+}
+EXPORT_SYMBOL(vme_lm_get);
+
+int vme_lm_attach(struct device *dev, int monitor, void (*callback)(int))
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if (bridge->lm_attach == NULL) {
+		printk("vme_lm_attach not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->lm_attach(monitor, callback);
+}
+EXPORT_SYMBOL(vme_lm_attach);
+
+int vme_lm_detach(struct device *dev, int monitor)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(dev);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if (bridge->lm_detach == NULL) {
+		printk("vme_lm_detach not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->lm_detach(monitor);
+}
+EXPORT_SYMBOL(vme_lm_detach);
+
+int vme_slot_get(struct device *bus)
+{
+	struct vme_bridge *bridge;
+
+	bridge = dev_to_bridge(bus);
+	if (bridge == NULL) {
+		printk(KERN_ERR "Can't find VME bus\n");
+		return -EINVAL;
+	}
+
+	if (bridge->slot_get == NULL) {
+		printk("vme_slot_get not supported\n");
+		return -EINVAL;
+	}
+
+	return bridge->slot_get();
+}
+EXPORT_SYMBOL(vme_slot_get);
+
+
+/* - Bridge Registration --------------------------------------------------- */
+
+static int vme_alloc_bus_num(void)
+{
+	int i;
+
+	down(&vme_bus_num_sem);
+	for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) {
+		if (((vme_bus_numbers >> i) & 0x1) == 0) {
+			vme_bus_numbers |= (0x1 << i);
+			break;
+		}
+	}
+	up(&vme_bus_num_sem);
+
+	return i;
+}
+
+static void vme_free_bus_num(int bus)
+{
+	down(&vme_bus_num_sem);
+	vme_bus_numbers |= ~(0x1 << bus);
+	up(&vme_bus_num_sem);
+}
+
+int vme_register_bridge (struct vme_bridge *bridge)
+{
+	struct device *dev;
+	int retval;
+	int i;
+
+	bridge->num = vme_alloc_bus_num();
+
+	/* This creates 32 vme "slot" devices. This equates to a slot for each
+	 * ID available in a system conforming to the ANSI/VITA 1-1994
+	 * specification.
+	 */
+	for (i = 0; i < VME_SLOTS_MAX; i++) {
+		dev = &(bridge->dev[i]);
+		memset(dev, 0, sizeof(struct device));
+
+		dev->parent = bridge->parent;
+		dev->bus = &(vme_bus_type);
+		/*
+		 * We save a pointer to the bridge in platform_data so that we
+		 * can get to it later. We keep driver_data for use by the
+		 * driver that binds against the slot
+		 */
+		dev->platform_data = bridge;
+		dev_set_name(dev, "vme-%x.%x", bridge->num, i + 1);
+
+		retval = device_register(dev);
+		if(retval)
+			goto err_reg;
+	}
+
+	return retval;
+
+	i = VME_SLOTS_MAX;
+err_reg:
+	while (i > -1) {
+		dev = &(bridge->dev[i]);
+		device_unregister(dev);
+	}
+	vme_free_bus_num(bridge->num);
+	return retval;
+}
+EXPORT_SYMBOL(vme_register_bridge);
+
+void vme_unregister_bridge (struct vme_bridge *bridge)
+{
+	int i;
+	struct device *dev;
+
+
+	for (i = 0; i < VME_SLOTS_MAX; i++) {
+		dev = &(bridge->dev[i]);
+		device_unregister(dev);
+	}
+	vme_free_bus_num(bridge->num);
+}
+EXPORT_SYMBOL(vme_unregister_bridge);
+
+
+/* - Driver Registration --------------------------------------------------- */
+
+int vme_register_driver (struct vme_driver *drv)
+{
+	drv->driver.name = drv->name;
+	drv->driver.bus = &vme_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(vme_register_driver);
+
+void vme_unregister_driver (struct vme_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(vme_unregister_driver);
+
+/* - Bus Registration ------------------------------------------------------ */
+
+int vme_calc_slot(struct device *dev)
+{
+	struct vme_bridge *bridge;
+	int num;
+
+	bridge = dev_to_bridge(dev);
+
+	/* Determine slot number */
+	num = 0;
+	while(num < VME_SLOTS_MAX) {
+		if(&(bridge->dev[num]) == dev) {
+			break;
+		}
+		num++;
+	}
+	if (num == VME_SLOTS_MAX) {
+		dev_err(dev, "Failed to identify slot\n");
+		num = 0;
+		goto err_dev;
+	}
+	num++;
+
+err_dev:
+	return num;
+}
+
+static struct vme_driver *dev_to_vme_driver(struct device *dev)
+{
+	if(dev->driver == NULL)
+		printk("Bugger dev->driver is NULL\n");
+
+	return container_of(dev->driver, struct vme_driver, driver);
+}
+
+static int vme_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct vme_bridge *bridge;
+	struct vme_driver *driver;
+	int i, num;
+
+	bridge = dev_to_bridge(dev);
+	driver = container_of(drv, struct vme_driver, driver);
+
+	num = vme_calc_slot(dev);
+	if (!num)
+		goto err_dev;
+
+	if (driver->bind_table == NULL) {
+		dev_err(dev, "Bind table NULL\n");
+		goto err_table;
+	}
+
+	i = 0;
+	while((driver->bind_table[i].bus != 0) ||
+		(driver->bind_table[i].slot != 0)) {
+
+		if ((bridge->num == driver->bind_table[i].bus) &&
+			(num == driver->bind_table[i].slot))
+			return 1;
+		i++;
+	}
+
+err_dev:
+err_table:
+	return 0;
+}
+
+static int vme_bus_probe(struct device *dev)
+{
+	struct vme_bridge *bridge;
+	struct vme_driver *driver;
+	int retval = -ENODEV;
+
+	driver = dev_to_vme_driver(dev);
+	bridge = dev_to_bridge(dev);
+
+	if(driver->probe != NULL) {
+		retval = driver->probe(dev, bridge->num, vme_calc_slot(dev));
+	}
+
+	return retval;
+}
+
+static int vme_bus_remove(struct device *dev)
+{
+	struct vme_bridge *bridge;
+	struct vme_driver *driver;
+	int retval = -ENODEV;
+
+	driver = dev_to_vme_driver(dev);
+	bridge = dev_to_bridge(dev);
+
+	if(driver->remove != NULL) {
+		retval = driver->remove(dev, bridge->num, vme_calc_slot(dev));
+	}
+
+	return retval;
+}
+
+struct bus_type vme_bus_type = {
+	.name = "vme",
+	.match = vme_bus_match,
+	.probe = vme_bus_probe,
+	.remove = vme_bus_remove,
+};
+EXPORT_SYMBOL(vme_bus_type);
+
+static int __init vme_init (void)
+{
+	return bus_register(&vme_bus_type);
+}
+
+static void __exit vme_exit (void)
+{
+	bus_unregister(&vme_bus_type);
+}
+
+MODULE_DESCRIPTION("VME bridge driver framework");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
+MODULE_LICENSE("GPL");
+
+module_init(vme_init);
+module_exit(vme_exit);
--- /dev/null
+++ b/drivers/staging/vme/vme.h
@@ -0,0 +1,153 @@
+#ifndef _VME_H_
+#define _VME_H_
+
+/* Resource Type */
+enum vme_resource_type {
+	VME_MASTER,
+	VME_SLAVE,
+	VME_DMA
+};
+
+/* VME Address Spaces */
+typedef u32 vme_address_t;
+#define VME_A16		0x1
+#define VME_A24		0x2
+#define	VME_A32		0x4
+#define VME_A64		0x8
+#define VME_CRCSR	0x10
+#define VME_USER1	0x20
+#define VME_USER2	0x40
+#define VME_USER3	0x80
+#define VME_USER4	0x100
+
+#define VME_A16_MAX	0x10000ULL
+#define VME_A24_MAX	0x1000000ULL
+#define VME_A32_MAX	0x100000000ULL
+#define VME_A64_MAX	0x10000000000000000ULL
+#define VME_CRCSR_MAX	0x1000000ULL
+
+
+/* VME Cycle Types */
+typedef u32 vme_cycle_t;
+#define VME_SCT		0x1
+#define VME_BLT		0x2
+#define VME_MBLT	0x4
+#define VME_2eVME	0x8
+#define VME_2eSST	0x10
+#define VME_2eSSTB	0x20
+
+#define VME_2eSST160	0x100
+#define VME_2eSST267	0x200
+#define VME_2eSST320	0x400
+
+#define	VME_SUPER	0x1000
+#define	VME_USER	0x2000
+#define	VME_PROG	0x4000
+#define	VME_DATA	0x8000
+
+/* VME Data Widths */
+typedef u32 vme_width_t;
+#define VME_D8		0x1
+#define VME_D16		0x2
+#define VME_D32		0x4
+#define VME_D64		0x8
+
+/* Arbitration Scheduling Modes */
+typedef u32 vme_arbitration_t;
+#define VME_R_ROBIN_MODE	0x1
+#define VME_PRIORITY_MODE	0x2
+
+typedef u32 vme_dma_t;
+#define VME_DMA_PATTERN			(1<<0)
+#define VME_DMA_PCI			(1<<1)
+#define VME_DMA_VME			(1<<2)
+
+typedef u32 vme_pattern_t;
+#define VME_DMA_PATTERN_BYTE		(1<<0)
+#define VME_DMA_PATTERN_WORD		(1<<1)
+#define VME_DMA_PATTERN_INCREMENT	(1<<2)
+
+struct vme_dma_attr {
+	vme_dma_t type;
+	void *private;
+};
+
+struct vme_resource {
+	enum vme_resource_type type;
+	struct list_head *entry;
+};
+
+extern struct bus_type vme_bus_type;
+
+struct vme_device_id {
+	int bus;
+	int slot;
+};
+
+struct vme_driver {
+	struct list_head node;
+	char *name;
+	const struct vme_device_id *bind_table;
+	int (*probe)  (struct device *, int, int);
+	int (*remove) (struct device *, int, int);
+	void (*shutdown) (void);
+	struct device_driver    driver;
+};
+
+void * vme_alloc_consistent(struct vme_resource *, size_t, dma_addr_t *);
+void vme_free_consistent(struct vme_resource *, size_t,  void *,
+	dma_addr_t);
+
+size_t vme_get_size(struct vme_resource *);
+
+struct vme_resource * vme_slave_request(struct device *, vme_address_t, vme_cycle_t);
+int vme_slave_set (struct vme_resource *, int, unsigned long long,
+	unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
+int vme_slave_get (struct vme_resource *, int *, unsigned long long *,
+	unsigned long long *, dma_addr_t *, vme_address_t *, vme_cycle_t *);
+void vme_slave_free(struct vme_resource *);
+
+struct vme_resource * vme_master_request(struct device *, vme_address_t, vme_cycle_t,
+	vme_width_t);
+int vme_master_set (struct vme_resource *, int, unsigned long long,
+	unsigned long long, vme_address_t, vme_cycle_t, vme_width_t);
+int vme_master_get (struct vme_resource *, int *, unsigned long long *,
+	unsigned long long *, vme_address_t *, vme_cycle_t *, vme_width_t *);
+ssize_t vme_master_read(struct vme_resource *, void *, size_t, loff_t);
+ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t);
+unsigned int vme_master_rmw (struct vme_resource *, unsigned int, unsigned int,
+	unsigned int, loff_t);
+void vme_master_free(struct vme_resource *);
+
+struct vme_resource *vme_request_dma(struct device *);
+struct vme_dma_list *vme_new_dma_list(struct vme_resource *);
+struct vme_dma_attr *vme_dma_pattern_attribute(u32, vme_pattern_t);
+struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t);
+struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long, vme_address_t,
+	vme_cycle_t, vme_width_t);
+void vme_dma_free_attribute(struct vme_dma_attr *);
+int vme_dma_list_add(struct vme_dma_list *, struct vme_dma_attr *,
+	struct vme_dma_attr *, size_t);
+int vme_dma_list_exec(struct vme_dma_list *);
+int vme_dma_list_free(struct vme_dma_list *);
+int vme_dma_free(struct vme_resource *);
+
+int vme_request_irq(struct device *, int, int,
+	void (*callback)(int, int, void *), void *);
+void vme_free_irq(struct device *, int, int);
+int vme_generate_irq(struct device *, int, int);
+
+int vme_lm_set(struct device *, unsigned long long, vme_address_t, vme_cycle_t);
+int vme_lm_get(struct device *, unsigned long long *, vme_address_t *,
+	vme_cycle_t *);
+int vme_lm_attach(struct device *, int, void (*callback)(int));
+int vme_lm_detach(struct device *, int);
+
+int vme_slot_get(struct device *);
+
+int vme_register_driver (struct vme_driver *);
+void vme_unregister_driver (struct vme_driver *);
+
+
+#endif /* _VME_H_ */
+


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

* [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
  2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
@ 2009-08-03 21:01   ` Greg K-H
  2009-08-08 23:22     ` Emilio G. Cota
  2009-08-03 21:01   ` [patch 3/5] Staging: vme: add Universe I/II bridge driver Greg K-H
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:01 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

[-- Attachment #1: staging-vme-add-vme-userspace-driver.patch --]
[-- Type: text/plain, Size: 23288 bytes --]

From: Martyn Welch <martyn.welch@gefanuc.com>

Adds a VME userspace access driver

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/staging/vme/Kconfig            |    4 
 drivers/staging/vme/Makefile           |    2 
 drivers/staging/vme/devices/Kconfig    |    8 
 drivers/staging/vme/devices/Makefile   |    5 
 drivers/staging/vme/devices/vme_user.c |  771 +++++++++++++++++++++++++++++++++
 drivers/staging/vme/devices/vme_user.h |   43 +
 6 files changed, 830 insertions(+), 3 deletions(-)

--- /dev/null
+++ b/drivers/staging/vme/devices/Kconfig
@@ -0,0 +1,8 @@
+comment "VME Device Drivers"
+
+config VME_USER
+	tristate "VME user space access driver"
+	help
+	  If you say Y here you want to be able to access a limited number of
+	  VME windows in a manner at least semi-compatible with the interface
+	  provided with the original driver at http://vmelinux.org/.
--- /dev/null
+++ b/drivers/staging/vme/devices/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the VME device drivers.
+#
+
+obj-$(CONFIG_VME_USER)		+= vme_user.o
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -0,0 +1,771 @@
+/*
+ * VMEbus User access driver
+ *
+ * Author: Martyn Welch <martyn.welch@gefanuc.com>
+ * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by:
+ *   Tom Armistead and Ajit Prem
+ *     Copyright 2004 Motorola Inc.
+ *
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/pci.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/syscalls.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "../vme.h"
+#include "vme_user.h"
+
+/* Currently Documentation/devices.txt defines the following for VME:
+ *
+ * 221 char	VME bus
+ * 		  0 = /dev/bus/vme/m0		First master image
+ * 		  1 = /dev/bus/vme/m1		Second master image
+ * 		  2 = /dev/bus/vme/m2		Third master image
+ * 		  3 = /dev/bus/vme/m3		Fourth master image
+ * 		  4 = /dev/bus/vme/s0		First slave image
+ * 		  5 = /dev/bus/vme/s1		Second slave image
+ * 		  6 = /dev/bus/vme/s2		Third slave image
+ * 		  7 = /dev/bus/vme/s3		Fourth slave image
+ * 		  8 = /dev/bus/vme/ctl		Control
+ *
+ * 		It is expected that all VME bus drivers will use the
+ * 		same interface.  For interface documentation see
+ * 		http://www.vmelinux.org/.
+ *
+ * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
+ * even support the tsi148 chipset (which has 8 master and 8 slave windows).
+ * We'll run with this or now as far as possible, however it probably makes
+ * sense to get rid of the old mappings and just do everything dynamically.
+ *
+ * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
+ * defined above and try to support at least some of the interface from
+ * http://www.vmelinux.org/ as an alternative drive can be written providing a
+ * saner interface later.
+ */
+#define VME_MAJOR	221	/* VME Major Device Number */
+#define VME_DEVS	9	/* Number of dev entries */
+
+#define MASTER_MINOR	0
+#define MASTER_MAX	3
+#define SLAVE_MINOR	4
+#define SLAVE_MAX	7
+#define CONTROL_MINOR	8
+
+#define PCI_BUF_SIZE  0x20000	/* Size of one slave image buffer */
+
+/*
+ * Structure to handle image related parameters.
+ */
+typedef struct {
+	void __iomem *kern_buf;	/* Buffer address in kernel space */
+	dma_addr_t pci_buf;	/* Buffer address in PCI address space */
+	unsigned long long size_buf;	/* Buffer size */
+	struct semaphore sem;	/* Semaphore for locking image */
+	struct device *device;	/* Sysfs device */
+	struct vme_resource *resource;	/* VME resource */
+	int users;		/* Number of current users */
+} image_desc_t;
+static image_desc_t image[VME_DEVS];
+
+typedef struct {
+	unsigned long reads;
+	unsigned long writes;
+	unsigned long ioctls;
+	unsigned long irqs;
+	unsigned long berrs;
+	unsigned long dmaErrors;
+	unsigned long timeouts;
+	unsigned long external;
+} driver_stats_t;
+static driver_stats_t statistics;
+
+struct cdev *vme_user_cdev;		/* Character device */
+struct class *vme_user_sysfs_class;	/* Sysfs class */
+struct device *vme_user_bridge;		/* Pointer to the bridge device */
+
+static char driver_name[] = "vme_user";
+
+static const int type[VME_DEVS] = {	MASTER_MINOR,	MASTER_MINOR,
+					MASTER_MINOR,	MASTER_MINOR,
+					SLAVE_MINOR,	SLAVE_MINOR,
+					SLAVE_MINOR,	SLAVE_MINOR,
+					CONTROL_MINOR
+				};
+
+
+static int vme_user_open(struct inode *, struct file *);
+static int vme_user_release(struct inode *, struct file *);
+static ssize_t vme_user_read(struct file *, char *, size_t, loff_t *);
+static ssize_t vme_user_write(struct file *, const char *, size_t, loff_t *);
+static loff_t vme_user_llseek(struct file *, loff_t, int);
+static int vme_user_ioctl(struct inode *, struct file *, unsigned int,
+	unsigned long);
+
+static int __init vme_user_probe(struct device *dev);
+
+static struct file_operations vme_user_fops = {
+        .open = vme_user_open,
+        .release = vme_user_release,
+        .read = vme_user_read,
+        .write = vme_user_write,
+        .llseek = vme_user_llseek,
+        .ioctl = vme_user_ioctl,
+};
+
+
+/*
+ * Reset all the statistic counters
+ */
+static void reset_counters(void)
+{
+        statistics.reads = 0;
+        statistics.writes = 0;
+        statistics.ioctls = 0;
+        statistics.irqs = 0;
+        statistics.berrs = 0;
+        statistics.dmaErrors = 0;
+        statistics.timeouts = 0;
+}
+
+void lmcall(int monitor)
+{
+	printk("Caught Location Monitor %d access\n", monitor);
+}
+
+static void tests(void)
+{
+	struct vme_resource *dma_res;
+	struct vme_dma_list *dma_list;
+	struct vme_dma_attr *pattern_attr, *vme_attr;
+
+	int retval;
+	unsigned int data;
+
+	printk("Running VME DMA test\n");
+	dma_res = vme_request_dma(vme_user_bridge);
+	dma_list = vme_new_dma_list(dma_res);
+	pattern_attr = vme_dma_pattern_attribute(0x0,
+		VME_DMA_PATTERN_WORD |
+			VME_DMA_PATTERN_INCREMENT);
+	vme_attr = vme_dma_vme_attribute(0x10000, VME_A32,
+		VME_SCT, VME_D32);
+	retval = vme_dma_list_add(dma_list, pattern_attr,
+		vme_attr, 0x10000);
+#if 0
+	vme_dma_free_attribute(vme_attr);
+	vme_attr = vme_dma_vme_attribute(0x20000, VME_A32,
+		VME_SCT, VME_D32);
+	retval = vme_dma_list_add(dma_list, pattern_attr,
+		vme_attr, 0x10000);
+#endif
+	retval = vme_dma_list_exec(dma_list);
+	vme_dma_free_attribute(pattern_attr);
+	vme_dma_free_attribute(vme_attr);
+	vme_dma_list_free(dma_list);
+#if 0
+	printk("Generating a VME interrupt\n");
+	vme_generate_irq(dma_res, 0x3, 0xaa);
+	printk("Interrupt returned\n");
+#endif
+	vme_dma_free(dma_res);
+
+	/* Attempt RMW */
+	data = vme_master_rmw(image[0].resource, 0x80000000, 0x00000000,
+		0x80000000, 0);
+	printk("RMW returned 0x%8.8x\n", data);
+
+
+	/* Location Monitor */
+	printk("vme_lm_set:%d\n", vme_lm_set(vme_user_bridge, 0x60000, VME_A32, VME_SCT | VME_USER | VME_DATA));
+	printk("vme_lm_attach:%d\n", vme_lm_attach(vme_user_bridge, 0, lmcall));
+
+	printk("Board in VME slot:%d\n", vme_slot_get(vme_user_bridge));
+}
+
+static int vme_user_open(struct inode *inode, struct file *file)
+{
+	int err;
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	down(&image[minor].sem);
+	/* Only allow device to be opened if a resource is allocated */
+	if (image[minor].resource == NULL) {
+		printk(KERN_ERR "No resources allocated for device\n");
+		err = -EINVAL;
+		goto err_res;
+	}
+
+	/* Increment user count */
+	image[minor].users++;
+
+	up(&image[minor].sem);
+
+	return 0;
+
+err_res:
+	up(&image[minor].sem);
+
+	return err;
+}
+
+static int vme_user_release(struct inode *inode, struct file *file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	down(&image[minor].sem);
+
+	/* Decrement user count */
+	image[minor].users--;
+
+	up(&image[minor].sem);
+
+	return 0;
+}
+
+/*
+ * We are going ot alloc a page during init per window for small transfers.
+ * Small transfers will go VME -> buffer -> user space. Larger (more than a
+ * page) transfers will lock the user space buffer into memory and then
+ * transfer the data directly into the user space buffers.
+ */
+static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
+	loff_t *ppos)
+{
+	ssize_t retval;
+	ssize_t copied = 0;
+
+	if (count <= image[minor].size_buf) {
+		/* We copy to kernel buffer */
+		copied = vme_master_read(image[minor].resource,
+			image[minor].kern_buf, count, *ppos);
+		if (copied < 0) {
+			return (int)copied;
+		}
+
+		retval = __copy_to_user(buf, image[minor].kern_buf,
+			(unsigned long)copied);
+		if (retval != 0) {
+			copied = (copied - retval);
+			printk("User copy failed\n");
+			return -EINVAL;
+		}
+
+	} else {
+		/* XXX Need to write this */
+		printk("Currently don't support large transfers\n");
+		/* Map in pages from userspace */
+
+		/* Call vme_master_read to do the transfer */
+		return -EINVAL;
+	}
+
+	return copied;
+}
+
+/*
+ * We are going ot alloc a page during init per window for small transfers.
+ * Small transfers will go user space -> buffer -> VME. Larger (more than a
+ * page) transfers will lock the user space buffer into memory and then
+ * transfer the data directly from the user space buffers out to VME.
+ */
+static ssize_t resource_from_user(unsigned int minor, const char *buf,
+	size_t count, loff_t *ppos)
+{
+	ssize_t retval;
+	ssize_t copied = 0;
+
+	if (count <= image[minor].size_buf) {
+		retval = __copy_from_user(image[minor].kern_buf, buf,
+			(unsigned long)count);
+		if (retval != 0)
+			copied = (copied - retval);
+		else
+			copied = count;
+
+		copied = vme_master_write(image[minor].resource,
+			image[minor].kern_buf, copied, *ppos);
+	} else {
+		/* XXX Need to write this */
+		printk("Currently don't support large transfers\n");
+		/* Map in pages from userspace */
+
+		/* Call vme_master_write to do the transfer */
+		return -EINVAL;
+	}
+
+	return copied;
+}
+
+static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	void __iomem *image_ptr;
+	ssize_t retval;
+
+	image_ptr = image[minor].kern_buf + *ppos;
+
+	retval = __copy_to_user(buf, image_ptr, (unsigned long)count);
+	if (retval != 0) {
+		retval = (count - retval);
+		printk(KERN_WARNING "Partial copy to userspace\n");
+	} else
+		retval = count;
+
+	/* Return number of bytes successfully read */
+	return retval;
+}
+
+static ssize_t buffer_from_user(unsigned int minor, const char *buf,
+	size_t count, loff_t *ppos)
+{
+	void __iomem *image_ptr;
+	size_t retval;
+
+	image_ptr = image[minor].kern_buf + *ppos;
+
+	retval = __copy_from_user(image_ptr, buf, (unsigned long)count);
+	if (retval != 0) {
+		retval = (count - retval);
+		printk(KERN_WARNING "Partial copy to userspace\n");
+	} else
+		retval = count;
+
+	/* Return number of bytes successfully read */
+	return retval;
+}
+
+static ssize_t vme_user_read(struct file *file, char *buf, size_t count,
+			loff_t * ppos)
+{
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	ssize_t retval;
+	size_t image_size;
+	size_t okcount;
+
+	down(&image[minor].sem);
+
+	/* XXX Do we *really* want this helper - we can use vme_*_get ? */
+	image_size = vme_get_size(image[minor].resource);
+
+	/* Ensure we are starting at a valid location */
+	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
+		up(&image[minor].sem);
+		return 0;
+	}
+
+	/* Ensure not reading past end of the image */
+	if (*ppos + count > image_size)
+		okcount = image_size - *ppos;
+	else
+		okcount = count;
+
+	switch (type[minor]){
+	case MASTER_MINOR:
+		retval = resource_to_user(minor, buf, okcount, ppos);
+		break;
+	case SLAVE_MINOR:
+		retval = buffer_to_user(minor, buf, okcount, ppos);
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	up(&image[minor].sem);
+
+	if (retval > 0)
+		*ppos += retval;
+
+	return retval;
+}
+
+static ssize_t vme_user_write(struct file *file, const char *buf, size_t count,
+			 loff_t *ppos)
+{
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	ssize_t retval;
+	size_t image_size;
+	size_t okcount;
+
+	down(&image[minor].sem);
+
+	image_size = vme_get_size(image[minor].resource);
+
+	/* Ensure we are starting at a valid location */
+	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
+		up(&image[minor].sem);
+		return 0;
+	}
+
+	/* Ensure not reading past end of the image */
+	if (*ppos + count > image_size)
+		okcount = image_size - *ppos;
+	else
+		okcount = count;
+
+	switch (type[minor]){
+	case MASTER_MINOR:
+		retval = resource_from_user(minor, buf, okcount, ppos);
+		break;
+	case SLAVE_MINOR:
+		retval = buffer_from_user(minor, buf, okcount, ppos);
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	up(&image[minor].sem);
+
+	if (retval > 0)
+		*ppos += retval;
+
+	return retval;
+}
+
+static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
+{
+	printk(KERN_ERR "Llseek currently incomplete\n");
+	return -EINVAL;
+}
+
+static int vme_user_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+#if 0
+	int ret_val;
+#endif
+	unsigned long copyRet;
+	vme_slave_t slave;
+
+	statistics.ioctls++;
+	switch (type[minor]) {
+	case CONTROL_MINOR:
+		break;
+	case MASTER_MINOR:
+		break;
+	case SLAVE_MINOR:
+		switch (cmd) {
+		case VME_SET_SLAVE:
+
+			copyRet = copy_from_user(&slave, (char *)arg,
+				sizeof(slave));
+			if (copyRet != 0) {
+				printk(KERN_WARNING "Partial copy from "
+					"userspace\n");
+				return -EFAULT;
+			}
+
+			return vme_slave_set(image[minor].resource,
+				slave.enable, slave.vme_addr, slave.size,
+				image[minor].pci_buf, slave.aspace,
+				slave.cycle);
+
+			break;
+#if 0
+		case VME_GET_SLAVE:
+			vme_slave_t slave;
+
+			ret_val = vme_slave_get(minor, &iRegs);
+
+			copyRet = copy_to_user((char *)arg, &slave,
+				sizeof(slave));
+			if (copyRet != 0) {
+				printk(KERN_WARNING "Partial copy to "
+					"userspace\n");
+				return -EFAULT;
+			}
+
+			return ret_val;
+			break;
+#endif
+		}
+		break;
+	}
+
+	return -EINVAL;
+}
+
+
+/*
+ * Unallocate a previously allocated buffer
+ */
+static void buf_unalloc (int num)
+{
+	if (image[num].kern_buf) {
+#ifdef VME_DEBUG
+		printk(KERN_DEBUG "UniverseII:Releasing buffer at %p\n",
+			image[num].pci_buf);
+#endif
+
+		vme_free_consistent(image[num].resource, image[num].size_buf,
+			image[num].kern_buf, image[num].pci_buf);
+
+		image[num].kern_buf = NULL;
+		image[num].pci_buf = 0;
+		image[num].size_buf = 0;
+
+#ifdef VME_DEBUG
+	} else {
+		printk(KERN_DEBUG "UniverseII: Buffer not allocated\n");
+#endif
+	}
+}
+
+static struct vme_driver vme_user_driver = {
+        .name = driver_name,
+        .probe = vme_user_probe,
+};
+
+
+/*
+ * In this simple access driver, the old behaviour is being preserved as much
+ * as practical. We will therefore reserve the buffers and request the images
+ * here so that we don't have to do it later.
+ */
+static int __init vme_bridge_init(void)
+{
+	int retval;
+	printk(KERN_INFO "VME User Space Access Driver\n");
+	printk("vme_user_driver:%p\n", &vme_user_driver);
+	retval = vme_register_driver(&vme_user_driver);
+	printk("vme_register_driver returned %d\n", retval);
+	return retval;
+}
+
+/*
+ * This structure gets passed a device, this should be the device created at
+ * registration.
+ */
+static int __init vme_user_probe(struct device *dev)
+{
+	int i, err;
+	char name[8];
+
+	printk("Running vme_user_probe()\n");
+
+	/* Pointer to the bridge device */
+	vme_user_bridge = dev;
+
+	/* Initialise descriptors */
+	for (i = 0; i < VME_DEVS; i++) {
+		image[i].kern_buf = NULL;
+		image[i].pci_buf = 0;
+		init_MUTEX(&(image[i].sem));
+		image[i].device = NULL;
+		image[i].resource = NULL;
+		image[i].users = 0;
+	}
+
+	/* Initialise statistics counters */
+	reset_counters();
+
+	/* Assign major and minor numbers for the driver */
+	err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS,
+		driver_name);
+	if (err) {
+		printk(KERN_WARNING "%s: Error getting Major Number %d for "
+		"driver.\n", driver_name, VME_MAJOR);
+		goto err_region;
+	}
+
+	/* Register the driver as a char device */
+	vme_user_cdev = cdev_alloc();
+	vme_user_cdev->ops = &vme_user_fops;
+	vme_user_cdev->owner = THIS_MODULE;
+	err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
+	if (err) {
+		printk(KERN_WARNING "%s: cdev_all failed\n", driver_name);
+		goto err_char;
+	}
+
+	/* Request slave resources and allocate buffers (128kB wide) */
+	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
+		/* XXX Need to properly request attributes */
+		image[i].resource = vme_slave_request(vme_user_bridge,
+			VME_A16, VME_SCT);
+		if (image[i].resource == NULL) {
+			printk(KERN_WARNING "Unable to allocate slave "
+				"resource\n");
+			goto err_buf;
+		}
+		image[i].size_buf = PCI_BUF_SIZE;
+		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
+			image[i].size_buf, &(image[i].pci_buf));
+		if (image[i].kern_buf == NULL) {
+			printk(KERN_WARNING "Unable to allocate memory for "
+				"buffer\n");
+			image[i].pci_buf = 0;
+			vme_slave_free(image[i].resource);
+			err = -ENOMEM;
+			goto err_buf;
+		}
+	}
+
+	/*
+	 * Request master resources allocate page sized buffers for small
+	 * reads and writes
+	 */
+	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
+		/* XXX Need to properly request attributes */
+		image[i].resource = vme_master_request(vme_user_bridge,
+			VME_A32, VME_SCT, VME_D32);
+		if (image[i].resource == NULL) {
+			printk(KERN_WARNING "Unable to allocate master "
+				"resource\n");
+			goto err_buf;
+		}
+		image[i].size_buf = PAGE_SIZE;
+		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
+			image[i].size_buf, &(image[i].pci_buf));
+		if (image[i].kern_buf == NULL) {
+			printk(KERN_WARNING "Unable to allocate memory for "
+				"buffer\n");
+			image[i].pci_buf = 0;
+			vme_master_free(image[i].resource);
+			err = -ENOMEM;
+			goto err_buf;
+		}
+	}
+
+	/* Setup some debug windows */
+	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
+		err = vme_slave_set(image[i].resource, 1, 0x4000*(i-4),
+			0x4000, image[i].pci_buf, VME_A16,
+			VME_SCT | VME_SUPER | VME_USER | VME_PROG | VME_DATA);
+		if (err != 0) {
+			printk(KERN_WARNING "Failed to configure window\n");
+			goto err_buf;
+		}
+	}
+	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
+		err = vme_master_set(image[i].resource, 1,
+			(0x10000 + (0x10000*i)), 0x10000,
+			VME_A32, VME_SCT | VME_USER | VME_DATA, VME_D32);
+		if (err != 0) {
+			printk(KERN_WARNING "Failed to configure window\n");
+			goto err_buf;
+		}
+	}
+
+	/* Create sysfs entries - on udev systems this creates the dev files */
+	vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
+	if (IS_ERR(vme_user_sysfs_class)) {
+		printk(KERN_ERR "Error creating vme_user class.\n");
+		err = PTR_ERR(vme_user_sysfs_class);
+		goto err_class;
+	}
+
+	/* Add sysfs Entries */
+	for (i=0; i<VME_DEVS; i++) {
+		switch (type[i]) {
+		case MASTER_MINOR:
+			sprintf(name,"bus/vme/m%%d");
+			break;
+		case CONTROL_MINOR:
+			sprintf(name,"bus/vme/ctl");
+			break;
+		case SLAVE_MINOR:
+			sprintf(name,"bus/vme/s%%d");
+			break;
+		default:
+			err = -EINVAL;
+			goto err_sysfs;
+			break;
+		}
+
+		image[i].device =
+			device_create(vme_user_sysfs_class, NULL,
+				MKDEV(VME_MAJOR, i), NULL, name,
+				(type[i] == SLAVE_MINOR)? i - (MASTER_MAX + 1) : i);
+		if (IS_ERR(image[i].device)) {
+			printk("%s: Error creating sysfs device\n",
+				driver_name);
+			err = PTR_ERR(image[i].device);
+			goto err_sysfs;
+		}
+	}
+
+	/* XXX Run tests */
+	/*
+	tests();
+	*/
+
+	return 0;
+
+	/* Ensure counter set correcty to destroy all sysfs devices */
+	i = VME_DEVS;
+err_sysfs:
+	while (i > 0){
+		i--;
+		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
+	}
+	class_destroy(vme_user_sysfs_class);
+
+	/* Ensure counter set correcty to unalloc all slave buffers */
+	i = SLAVE_MAX + 1;
+err_buf:
+	while (i > SLAVE_MINOR){
+		i--;
+		vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
+		vme_slave_free(image[i].resource);
+		buf_unalloc(i);
+	}
+err_class:
+	cdev_del(vme_user_cdev);
+err_char:
+	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
+err_region:
+	return err;
+}
+
+static void __exit vme_bridge_exit(void)
+{
+	int i;
+
+	/* Remove sysfs Entries */
+	for(i=0; i<VME_DEVS; i++) {
+		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
+	}
+	class_destroy(vme_user_sysfs_class);
+
+	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
+		buf_unalloc(i);
+	}
+
+	/* Unregister device driver */
+	cdev_del(vme_user_cdev);
+
+	/* Unregiser the major and minor device numbers */
+	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
+}
+
+MODULE_DESCRIPTION("VME User Space Access Driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
+MODULE_LICENSE("GPL");
+
+module_init(vme_bridge_init);
+module_exit(vme_bridge_exit);
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_user.h
@@ -0,0 +1,43 @@
+
+/*
+ * VMEbus Master Window Configuration Structure
+ */
+typedef struct {
+	char enable;			/* State of Window */
+	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
+	unsigned long long size;	/* Window Size */
+	vme_address_t aspace;		/* Address Space */
+	vme_cycle_t cycle;		/* Cycle properties */
+	vme_width_t dwidth;		/* Maximum Data Width */
+#if 0
+	char prefetchEnable;		/* Prefetch Read Enable State */
+	int prefetchSize;		/* Prefetch Read Size (Cache Lines) */
+	char wrPostEnable;		/* Write Post State */
+#endif
+} vme_master_t;
+
+
+/*
+ * IOCTL Commands and structures
+ */
+
+/* Magic number for use in ioctls */
+#define VME_IOC_MAGIC 0xAE
+
+
+/* VMEbus Slave Window Configuration Structure */
+typedef struct {
+	char enable;			/* State of Window */
+	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
+	unsigned long long size;	/* Window Size */
+	vme_address_t aspace;		/* Address Space */
+	vme_cycle_t cycle;		/* Cycle properties */
+#if 0
+	char wrPostEnable;		/* Write Post State */
+	char rmwLock;			/* Lock PCI during RMW Cycles */
+	char data64BitCapable;		/* non-VMEbus capable of 64-bit Data */
+#endif
+} vme_slave_t;
+
+#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 10, vme_slave_t)
+
--- a/drivers/staging/vme/Kconfig
+++ b/drivers/staging/vme/Kconfig
@@ -11,7 +11,7 @@ menuconfig VME
 if VME
 
 #source "drivers/staging/vme/bridges/Kconfig"
-#
-#source "drivers/staging/vme/devices/Kconfig"
+
+source "drivers/staging/vme/devices/Kconfig"
 
 endif # VME
--- a/drivers/staging/vme/Makefile
+++ b/drivers/staging/vme/Makefile
@@ -4,4 +4,4 @@
 obj-$(CONFIG_VME)		+= vme.o
 
 #obj-y				+= bridges/
-#obj-y				+= devices/
+obj-y				+= devices/


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

* [patch 3/5] Staging: vme: add Universe I/II bridge driver
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
  2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
  2009-08-03 21:01   ` [patch 2/5] Staging: vme: add VME userspace driver Greg K-H
@ 2009-08-03 21:01   ` Greg K-H
  2009-08-03 23:00     ` Jiri Slaby
  2009-08-03 21:01   ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Greg K-H
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:01 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

[-- Attachment #1: staging-vme-add-universe-i-ii-bridge-driver.patch --]
[-- Type: text/plain, Size: 63575 bytes --]

From: Martyn Welch <martyn.welch@gefanuc.com>

Currently this code doesn't compile, so it is disabled.

That should be fixed up...

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/staging/vme/Kconfig                |    2 
 drivers/staging/vme/Makefile               |    2 
 drivers/staging/vme/bridges/Kconfig        |    8 
 drivers/staging/vme/bridges/Makefile       |    1 
 drivers/staging/vme/bridges/vme_ca91cx42.c | 1811 +++++++++++++++++++++++++++++
 drivers/staging/vme/bridges/vme_ca91cx42.h |  403 ++++++
 6 files changed, 2225 insertions(+), 2 deletions(-)

--- /dev/null
+++ b/drivers/staging/vme/bridges/Kconfig
@@ -0,0 +1,8 @@
+comment "VME Bridge Drivers"
+
+config VME_CA91CX42
+	tristate "Universe I/II"
+	depends on BROKEN
+	help
+	 If you say Y here you get support for the Tundra CA91C042 (Universe I)
+	 and CA91C142 (Universe II) VME bridge chips.
--- /dev/null
+++ b/drivers/staging/vme/bridges/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VME_CA91CX42)	+= vme_ca91cx42.o
--- /dev/null
+++ b/drivers/staging/vme/bridges/vme_ca91cx42.c
@@ -0,0 +1,1811 @@
+/*
+ * Support for the Tundra Universe I/II VME-PCI Bridge Chips
+ *
+ * Author: Martyn Welch <martyn.welch@gefanuc.com>
+ * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by Tom Armistead and Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * Derived from ca91c042.c by Michael Wyrick
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "../vme.h"
+#include "../vme_bridge.h"
+#include "vme_ca91cx42.h"
+
+extern struct vmeSharedData *vmechip_interboard_data;
+extern dma_addr_t vmechip_interboard_datap;
+extern const int vmechip_revision;
+extern const int vmechip_devid;
+extern const int vmechip_irq;
+extern int vmechip_irq_overhead_ticks;
+extern char *vmechip_baseaddr;
+extern const int vme_slotnum;
+extern int vme_syscon;
+extern unsigned int out_image_va[];
+extern unsigned int vme_irqlog[8][0x100];
+
+static int outCTL[] = { LSI0_CTL, LSI1_CTL, LSI2_CTL, LSI3_CTL,
+	LSI4_CTL, LSI5_CTL, LSI6_CTL, LSI7_CTL
+};
+
+static int outBS[] = { LSI0_BS, LSI1_BS, LSI2_BS, LSI3_BS,
+	LSI4_BS, LSI5_BS, LSI6_BS, LSI7_BS
+};
+
+static int outBD[] = { LSI0_BD, LSI1_BD, LSI2_BD, LSI3_BD,
+	LSI4_BD, LSI5_BD, LSI6_BD, LSI7_BD
+};
+
+static int outTO[] = { LSI0_TO, LSI1_TO, LSI2_TO, LSI3_TO,
+	LSI4_TO, LSI5_TO, LSI6_TO, LSI7_TO
+};
+
+static int inCTL[] = { VSI0_CTL, VSI1_CTL, VSI2_CTL, VSI3_CTL,
+	VSI4_CTL, VSI5_CTL, VSI6_CTL, VSI7_CTL
+};
+
+static int inBS[] = { VSI0_BS, VSI1_BS, VSI2_BS, VSI3_BS,
+	VSI4_BS, VSI5_BS, VSI6_BS, VSI7_BS
+};
+
+static int inBD[] = { VSI0_BD, VSI1_BD, VSI2_BD, VSI3_BD,
+	VSI4_BD, VSI5_BD, VSI6_BD, VSI7_BD
+};
+
+static int inTO[] = { VSI0_TO, VSI1_TO, VSI2_TO, VSI3_TO,
+	VSI4_TO, VSI5_TO, VSI6_TO, VSI7_TO
+};
+static int vmevec[7] = { V1_STATID, V2_STATID, V3_STATID, V4_STATID,
+	V5_STATID, V6_STATID, V7_STATID
+};
+
+struct interrupt_counters {
+	unsigned int acfail;
+	unsigned int sysfail;
+	unsigned int sw_int;
+	unsigned int sw_iack;
+	unsigned int verr;
+	unsigned int lerr;
+	unsigned int lm;
+	unsigned int mbox;
+	unsigned int dma;
+	unsigned int virq[7];
+	unsigned int vown;
+};
+
+extern wait_queue_head_t dma_queue[];
+extern wait_queue_head_t lm_queue;
+extern wait_queue_head_t mbox_queue;
+
+extern int tb_speed;
+
+unsigned int uni_irq_time;
+unsigned int uni_dma_irq_time;
+unsigned int uni_lm_event;
+
+static spinlock_t lm_lock = SPIN_LOCK_UNLOCKED;
+
+static struct interrupt_counters Interrupt_counters = { 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0,
+	{0, 0, 0, 0, 0, 0, 0},
+	0
+};
+
+#define read_register(offset) readl(vmechip_baseaddr + offset)
+#define write_register(value,offset) writel(value, vmechip_baseaddr + offset)
+#define read_register_word(offset) readw(vmechip_baseaddr + offset)
+#define write_register_word(value,offset) writew(value, vmechip_baseaddr + offset)
+
+int uni_procinfo(char *buf)
+{
+	char *p;
+
+	p = buf;
+
+	p += sprintf(p, "\n");
+	{
+		unsigned long misc_ctl;
+
+		misc_ctl = read_register(MISC_CTL);
+		p += sprintf(p, "MISC_CTL:\t\t\t0x%08lx\n", misc_ctl);
+		p += sprintf(p, "VME Bus Time Out:\t\t");
+		switch ((misc_ctl & UNIV_BM_MISC_CTL_VBTO) >>
+			UNIV_OF_MISC_CTL_VBTO) {
+		case 0x0:
+			p += sprintf(p, "Disabled\n");
+			break;
+		case 0x1:
+			p += sprintf(p, "16 us\n");
+			break;
+		case 0x2:
+			p += sprintf(p, "32 us\n");
+			break;
+		case 0x3:
+			p += sprintf(p, "64 us\n");
+			break;
+		case 0x4:
+			p += sprintf(p, "128 us\n");
+			break;
+		case 0x5:
+			p += sprintf(p, "256 us\n");
+			break;
+		case 0x6:
+			p += sprintf(p, "512 us\n");
+			break;
+		case 0x7:
+			p += sprintf(p, "1024 us\n");
+			break;
+		default:
+			p += sprintf(p, "Reserved Value, Undefined\n");
+		}
+		p += sprintf(p, "VME Arbitration Time Out:\t");
+		switch ((misc_ctl & UNIV_BM_MISC_CTL_VARBTO) >>
+			UNIV_OF_MISC_CTL_VARBTO) {
+		case 0x0:
+			p += sprintf(p, "Disabled");
+			break;
+		case 0x1:
+			p += sprintf(p, "16 us");
+			break;
+		case 0x2:
+			p += sprintf(p, "256 us");
+			break;
+		default:
+			p += sprintf(p, "Reserved Value, Undefined");
+		}
+		if (misc_ctl & UNIV_BM_MISC_CTL_VARB)
+			p += sprintf(p, ", Priority Arbitration\n");
+		else
+			p += sprintf(p, ", Round Robin Arbitration\n");
+		p += sprintf(p, "\n");
+	}
+
+	{
+		unsigned int lmisc;
+		unsigned int crt;
+		unsigned int cwt;
+
+		lmisc = read_register(LMISC);
+		p += sprintf(p, "LMISC:\t\t\t\t0x%08x\n", lmisc);
+		crt = (lmisc & UNIV_BM_LMISC_CRT) >> UNIV_OF_LMISC_CRT;
+		cwt = (lmisc & UNIV_BM_LMISC_CWT) >> UNIV_OF_LMISC_CWT;
+		p += sprintf(p, "Coupled Request Timer:\t\t");
+		switch (crt) {
+		case 0x0:
+			p += sprintf(p, "Disabled\n");
+			break;
+		case 0x1:
+			p += sprintf(p, "128 us\n");
+			break;
+		case 0x2:
+			p += sprintf(p, "256 us\n");
+			break;
+		case 0x3:
+			p += sprintf(p, "512 us\n");
+			break;
+		case 0x4:
+			p += sprintf(p, "1024 us\n");
+			break;
+		case 0x5:
+			p += sprintf(p, "2048 us\n");
+			break;
+		case 0x6:
+			p += sprintf(p, "4096 us\n");
+			break;
+		default:
+			p += sprintf(p, "Reserved\n");
+		}
+		p += sprintf(p, "Coupled Window Timer:\t\t");
+		switch (cwt) {
+		case 0x0:
+			p += sprintf(p, "Disabled\n");
+			break;
+		case 0x1:
+			p += sprintf(p, "16 PCI Clocks\n");
+			break;
+		case 0x2:
+			p += sprintf(p, "32 PCI Clocks\n");
+			break;
+		case 0x3:
+			p += sprintf(p, "64 PCI Clocks\n");
+			break;
+		case 0x4:
+			p += sprintf(p, "128 PCI Clocks\n");
+			break;
+		case 0x5:
+			p += sprintf(p, "256 PCI Clocks\n");
+			break;
+		case 0x6:
+			p += sprintf(p, "512 PCI Clocks\n");
+			break;
+		default:
+			p += sprintf(p, "Reserved\n");
+		}
+		p += sprintf(p, "\n");
+	}
+	{
+		unsigned int mast_ctl;
+
+		mast_ctl = read_register(MAST_CTL);
+		p += sprintf(p, "MAST_CTL:\t\t\t0x%08x\n", mast_ctl);
+		{
+			int retries;
+
+			retries = ((mast_ctl & UNIV_BM_MAST_CTL_MAXRTRY)
+				   >> UNIV_OF_MAST_CTL_MAXRTRY) * 64;
+			p += sprintf(p, "Max PCI Master Retries:\t\t");
+			if (retries)
+				p += sprintf(p, "%d\n", retries);
+			else
+				p += sprintf(p, "Forever\n");
+		}
+
+		p += sprintf(p, "Posted Write Transfer Count:\t");
+		switch ((mast_ctl & UNIV_BM_MAST_CTL_PWON) >>
+			UNIV_OF_MAST_CTL_PWON) {
+		case 0x0:
+			p += sprintf(p, "128 Bytes\n");
+			break;
+		case 0x1:
+			p += sprintf(p, "256 Bytes\n");
+			break;
+		case 0x2:
+			p += sprintf(p, "512 Bytes\n");
+			break;
+		case 0x3:
+			p += sprintf(p, "1024 Bytes\n");
+			break;
+		case 0x4:
+			p += sprintf(p, "2048 Bytes\n");
+			break;
+		case 0x5:
+			p += sprintf(p, "4096 Bytes\n");
+			break;
+		default:
+			p += sprintf(p, "Undefined\n");
+		}
+
+		p += sprintf(p, "VMEbus Request Level:\t\t");
+		switch ((mast_ctl & UNIV_BM_MAST_CTL_VRL) >>
+			UNIV_OF_MAST_CTL_VRL) {
+		case 0x0:
+			p += sprintf(p, "Level 0\n");
+		case 0x1:
+			p += sprintf(p, "Level 1\n");
+		case 0x2:
+			p += sprintf(p, "Level 2\n");
+		case 0x3:
+			p += sprintf(p, "Level 3\n");
+		}
+		p += sprintf(p, "VMEbus Request Mode:\t\t");
+		if (mast_ctl & UNIV_BM_MAST_CTL_VRM)
+			p += sprintf(p, "Fair Request Mode\n");
+		else
+			p += sprintf(p, "Demand Request Mode\n");
+		p += sprintf(p, "VMEbus Release Mode:\t\t");
+		if (mast_ctl & UNIV_BM_MAST_CTL_VREL)
+			p += sprintf(p, "Release on Request\n");
+		else
+			p += sprintf(p, "Release when Done\n");
+		p += sprintf(p, "VMEbus Ownership Bit:\t\t");
+		if (mast_ctl & UNIV_BM_MAST_CTL_VOWN)
+			p += sprintf(p, "Acquire and hold VMEbus\n");
+		else
+			p += sprintf(p, "Release VMEbus\n");
+		p += sprintf(p, "VMEbus Ownership Bit Ack:\t");
+		if (mast_ctl & UNIV_BM_MAST_CTL_VOWN_ACK)
+			p += sprintf(p, "Owning VMEbus\n");
+		else
+			p += sprintf(p, "Not Owning VMEbus\n");
+		p += sprintf(p, "\n");
+	}
+	{
+		unsigned int misc_stat;
+
+		misc_stat = read_register(MISC_STAT);
+		p += sprintf(p, "MISC_STAT:\t\t\t0x%08x\n", misc_stat);
+		p += sprintf(p, "Universe BBSY:\t\t\t");
+		if (misc_stat & UNIV_BM_MISC_STAT_MYBBSY)
+			p += sprintf(p, "Negated\n");
+		else
+			p += sprintf(p, "Asserted\n");
+		p += sprintf(p, "Transmit FIFO:\t\t\t");
+		if (misc_stat & UNIV_BM_MISC_STAT_TXFE)
+			p += sprintf(p, "Empty\n");
+		else
+			p += sprintf(p, "Not empty\n");
+		p += sprintf(p, "Receive FIFO:\t\t\t");
+		if (misc_stat & UNIV_BM_MISC_STAT_RXFE)
+			p += sprintf(p, "Empty\n");
+		else
+			p += sprintf(p, "Not Empty\n");
+		p += sprintf(p, "\n");
+	}
+
+	p += sprintf(p, "Latency Timer:\t\t\t%02d Clocks\n\n",
+		     (read_register(UNIV_PCI_MISC0) &
+		      UNIV_BM_PCI_MISC0_LTIMER) >> UNIV_OF_PCI_MISC0_LTIMER);
+
+	{
+		unsigned int lint_en;
+		unsigned int lint_stat;
+
+		lint_en = read_register(LINT_EN);
+		lint_stat = read_register(LINT_STAT);
+
+#define REPORT_IRQ(name,field)     \
+    p += sprintf(p, (lint_en & UNIV_BM_LINT_##name) ? "Enabled" : "Masked"); \
+    p += sprintf(p, ", triggered %d times", Interrupt_counters.field); \
+    p += sprintf(p, (lint_stat & UNIV_BM_LINT_##name) ? ", irq now active\n" : "\n");
+		p += sprintf(p, "ACFAIL Interrupt:\t\t");
+		REPORT_IRQ(ACFAIL, acfail);
+		p += sprintf(p, "SYSFAIL Interrupt:\t\t");
+		REPORT_IRQ(SYSFAIL, sysfail);
+		p += sprintf(p, "SW_INT Interrupt:\t\t");
+		REPORT_IRQ(SW_INT, sw_int);
+		p += sprintf(p, "SW_IACK Interrupt:\t\t");
+		REPORT_IRQ(SW_IACK, sw_iack);
+		p += sprintf(p, "VERR Interrupt:\t\t\t");
+		REPORT_IRQ(VERR, verr);
+		p += sprintf(p, "LERR Interrupt:\t\t\t");
+		REPORT_IRQ(LERR, lerr);
+		p += sprintf(p, "LM Interrupt:\t\t\t");
+		REPORT_IRQ(LM, lm);
+		p += sprintf(p, "MBOX Interrupt:\t\t\t");
+		REPORT_IRQ(MBOX, mbox);
+		p += sprintf(p, "DMA Interrupt:\t\t\t");
+		REPORT_IRQ(DMA, dma);
+		p += sprintf(p, "VIRQ7 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ7, virq[7 - 1]);
+		p += sprintf(p, "VIRQ6 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ6, virq[6 - 1]);
+		p += sprintf(p, "VIRQ5 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ5, virq[5 - 1]);
+		p += sprintf(p, "VIRQ4 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ4, virq[4 - 1]);
+		p += sprintf(p, "VIRQ3 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ3, virq[3 - 1]);
+		p += sprintf(p, "VIRQ2 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ2, virq[2 - 1]);
+		p += sprintf(p, "VIRQ1 Interrupt:\t\t");
+		REPORT_IRQ(VIRQ1, virq[1 - 1]);
+		p += sprintf(p, "VOWN Interrupt:\t\t\t");
+		REPORT_IRQ(VOWN, vown);
+		p += sprintf(p, "\n");
+#undef REPORT_IRQ
+	}
+	{
+		unsigned long vrai_ctl;
+
+		vrai_ctl = read_register(VRAI_CTL);
+		if (vrai_ctl & UNIV_BM_VRAI_CTL_EN) {
+			unsigned int vrai_bs;
+
+			vrai_bs = read_register(VRAI_BS);
+			p += sprintf(p,
+				     "VME Register Image:\t\tEnabled at VME-Address 0x%x\n",
+				     vrai_bs);
+		} else
+			p += sprintf(p, "VME Register Image:\t\tDisabled\n");
+	}
+	{
+		unsigned int slsi;
+
+		slsi = read_register(SLSI);
+		if (slsi & UNIV_BM_SLSI_EN) {
+			/* Not implemented */
+		} else {
+			p += sprintf(p, "Special PCI Slave Image:\tDisabled\n");
+		}
+	}
+	{
+		int i;
+
+		for (i = 0; i < (vmechip_revision > 0 ? 8 : 4); i++) {
+			unsigned int ctl, bs, bd, to, vstart, vend;
+
+			ctl = readl(vmechip_baseaddr + outCTL[i]);
+			bs = readl(vmechip_baseaddr + outBS[i]);
+			bd = readl(vmechip_baseaddr + outBD[i]);
+			to = readl(vmechip_baseaddr + outTO[i]);
+
+			vstart = bs + to;
+			vend = bd + to;
+
+			p += sprintf(p, "PCI Slave Image %d:\t\t", i);
+			if (ctl & UNIV_BM_LSI_CTL_EN) {
+				p += sprintf(p, "Enabled");
+				if (ctl & UNIV_BM_LSI_CTL_PWEN)
+					p += sprintf(p,
+						     ", Posted Write Enabled\n");
+				else
+					p += sprintf(p, "\n");
+				p += sprintf(p,
+					     "\t\t\t\tPCI Addresses from 0x%x to 0x%x\n",
+					     bs, bd);
+				p += sprintf(p,
+					     "\t\t\t\tVME Addresses from 0x%x to 0x%x\n",
+					     vstart, vend);
+			} else
+				p += sprintf(p, "Disabled\n");
+		}
+		p += sprintf(p, "\n");
+	}
+	{
+		int i;
+		for (i = 0; i < (vmechip_revision > 0 ? 8 : 4); i++) {
+			unsigned int ctl, bs, bd, to, vstart, vend;
+
+			ctl = readl(vmechip_baseaddr + inCTL[i]);
+			bs = readl(vmechip_baseaddr + inBS[i]);
+			bd = readl(vmechip_baseaddr + inBD[i]);
+			to = readl(vmechip_baseaddr + inTO[i]);
+			vstart = bs + to;
+			vend = bd + to;
+			p += sprintf(p, "VME Slave Image %d:\t\t", i);
+			if (ctl & UNIV_BM_LSI_CTL_EN) {
+				p += sprintf(p, "Enabled");
+				if (ctl & UNIV_BM_LSI_CTL_PWEN)
+					p += sprintf(p,
+						     ", Posted Write Enabled\n");
+				else
+					p += sprintf(p, "\n");
+				p += sprintf(p,
+					     "\t\t\t\tVME Addresses from 0x%x to 0x%x\n",
+					     bs, bd);
+				p += sprintf(p,
+					     "\t\t\t\tPCI Addresses from 0x%x to 0x%x\n",
+					     vstart, vend);
+			} else
+				p += sprintf(p, "Disabled\n");
+		}
+	}
+
+	return p - buf;
+}
+
+//----------------------------------------------------------------------------
+//  uni_bus_error_chk()
+//----------------------------------------------------------------------------
+int uni_bus_error_chk(int clrflag)
+{
+	int tmp;
+	tmp = readl(vmechip_baseaddr + PCI_COMMAND);
+	if (tmp & 0x08000000) {	// S_TA is Set
+		if (clrflag)
+			writel(tmp | 0x08000000,
+			       vmechip_baseaddr + PCI_COMMAND);
+		return (1);
+	}
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : DMA_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description: Saves DMA completion timestamp and then wakes up DMA queue
+//-----------------------------------------------------------------------------
+static void DMA_uni_irqhandler(void)
+{
+	uni_dma_irq_time = uni_irq_time;
+	wake_up(&dma_queue[0]);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : LERR_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static void LERR_uni_irqhandler(void)
+{
+	int val;
+
+	val = readl(vmechip_baseaddr + DGCS);
+
+	if (!(val & 0x00000800)) {
+		printk(KERN_ERR
+		       "ca91c042: LERR_uni_irqhandler DMA Read Error DGCS=%08X\n",
+		       val);
+
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Function   : VERR_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static void VERR_uni_irqhandler(void)
+{
+	int val;
+
+	val = readl(vmechip_baseaddr + DGCS);
+
+	if (!(val & 0x00000800)) {
+		printk(KERN_ERR
+		       "ca91c042: VERR_uni_irqhandler DMA Read Error DGCS=%08X\n",
+		       val);
+	}
+
+}
+
+//-----------------------------------------------------------------------------
+// Function   : MB_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static void MB_uni_irqhandler(int mbox_mask)
+{
+	if (vmechip_irq_overhead_ticks != 0) {
+		wake_up(&mbox_queue);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Function   : LM_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static void LM_uni_irqhandler(int lm_mask)
+{
+	uni_lm_event = lm_mask;
+	wake_up(&lm_queue);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : VIRQ_uni_irqhandler
+// Inputs     : void
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static void VIRQ_uni_irqhandler(int virq_mask)
+{
+	int iackvec, i;
+
+	for (i = 7; i > 0; i--) {
+		if (virq_mask & (1 << i)) {
+			Interrupt_counters.virq[i - 1]++;
+			iackvec = readl(vmechip_baseaddr + vmevec[i - 1]);
+			vme_irqlog[i][iackvec]++;
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_irqhandler
+// Inputs     : int irq, void *dev_id, struct pt_regs *regs
+// Outputs    : void
+// Description:
+//-----------------------------------------------------------------------------
+static irqreturn_t uni_irqhandler(int irq, void *dev_id)
+{
+	long stat, enable;
+
+	if (dev_id != vmechip_baseaddr)
+		return IRQ_NONE;
+
+	uni_irq_time = get_tbl();
+
+	stat = readl(vmechip_baseaddr + LINT_STAT);
+	writel(stat, vmechip_baseaddr + LINT_STAT);	// Clear all pending ints
+	enable = readl(vmechip_baseaddr + LINT_EN);
+	stat = stat & enable;
+	if (stat & 0x0100) {
+		Interrupt_counters.dma++;
+		DMA_uni_irqhandler();
+	}
+	if (stat & 0x0200) {
+		Interrupt_counters.lerr++;
+		LERR_uni_irqhandler();
+	}
+	if (stat & 0x0400) {
+		Interrupt_counters.verr++;
+		VERR_uni_irqhandler();
+	}
+	if (stat & 0xF0000) {
+		Interrupt_counters.mbox++;
+		MB_uni_irqhandler((stat & 0xF0000) >> 16);
+	}
+	if (stat & 0xF00000) {
+		Interrupt_counters.lm++;
+		LM_uni_irqhandler((stat & 0xF00000) >> 20);
+	}
+	if (stat & 0x0000FE) {
+		VIRQ_uni_irqhandler(stat & 0x0000FE);
+	}
+	if (stat & UNIV_BM_LINT_ACFAIL) {
+		Interrupt_counters.acfail++;
+	}
+	if (stat & UNIV_BM_LINT_SYSFAIL) {
+		Interrupt_counters.sysfail++;
+	}
+	if (stat & UNIV_BM_LINT_SW_INT) {
+		Interrupt_counters.sw_int++;
+	}
+	if (stat & UNIV_BM_LINT_SW_IACK) {
+		Interrupt_counters.sw_iack++;
+	}
+	if (stat & UNIV_BM_LINT_VOWN) {
+		Interrupt_counters.vown++;
+	}
+
+	return IRQ_HANDLED;
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_generate_irq
+// Description:
+//-----------------------------------------------------------------------------
+int uni_generate_irq(virqInfo_t * vmeIrq)
+{
+	int timeout;
+	int looptimeout;
+
+	timeout = vmeIrq->waitTime;
+	if (timeout == 0) {
+		timeout++;	// Wait at least 1 tick...
+	}
+	looptimeout = HZ / 20;	// try for 1/20 second
+
+	vmeIrq->timeOutFlag = 0;
+
+	// Validate & setup vector register.
+	if (vmeIrq->vector & 1) {	// Universe can only generate even vectors
+		return (-EINVAL);
+	}
+	writel(vmeIrq->vector << 24, vmechip_baseaddr + STATID);
+
+	// Assert VMEbus IRQ
+	writel(1 << (vmeIrq->level + 24), vmechip_baseaddr + VINT_EN);
+
+	// Wait for syscon to do iack
+	while (readl(vmechip_baseaddr + VINT_STAT) &
+	       (1 << (vmeIrq->level + 24))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(looptimeout);
+		timeout = timeout - looptimeout;
+		if (timeout <= 0) {
+			vmeIrq->timeOutFlag = 1;
+			break;
+		}
+	}
+
+	// Clear VMEbus IRQ bit
+	writel(0, vmechip_baseaddr + VINT_EN);
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_set_arbiter
+// Description:
+//-----------------------------------------------------------------------------
+int uni_set_arbiter(vmeArbiterCfg_t * vmeArb)
+{
+	int temp_ctl = 0;
+	int vbto = 0;
+
+	temp_ctl = readl(vmechip_baseaddr + MISC_CTL);
+	temp_ctl &= 0x00FFFFFF;
+
+	if (vmeArb->globalTimeoutTimer == 0xFFFFFFFF) {
+		vbto = 7;
+	} else if (vmeArb->globalTimeoutTimer > 1024) {
+		return (-EINVAL);
+	} else if (vmeArb->globalTimeoutTimer == 0) {
+		vbto = 0;
+	} else {
+		vbto = 1;
+		while ((16 * (1 << (vbto - 1))) < vmeArb->globalTimeoutTimer) {
+			vbto += 1;
+		}
+	}
+	temp_ctl |= (vbto << 28);
+
+	if (vmeArb->arbiterMode == VME_PRIORITY_MODE) {
+		temp_ctl |= 1 << 26;
+	}
+
+	if (vmeArb->arbiterTimeoutFlag) {
+		temp_ctl |= 2 << 24;
+	}
+
+	writel(temp_ctl, vmechip_baseaddr + MISC_CTL);
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_get_arbiter
+// Description:
+//-----------------------------------------------------------------------------
+int uni_get_arbiter(vmeArbiterCfg_t * vmeArb)
+{
+	int temp_ctl = 0;
+	int vbto = 0;
+
+	temp_ctl = readl(vmechip_baseaddr + MISC_CTL);
+
+	vbto = (temp_ctl >> 28) & 0xF;
+	if (vbto != 0) {
+		vmeArb->globalTimeoutTimer = (16 * (1 << (vbto - 1)));
+	}
+
+	if (temp_ctl & (1 << 26)) {
+		vmeArb->arbiterMode = VME_PRIORITY_MODE;
+	} else {
+		vmeArb->arbiterMode = VME_R_ROBIN_MODE;
+	}
+
+	if (temp_ctl & (3 << 24)) {
+		vmeArb->arbiterTimeoutFlag = 1;
+	}
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_set_requestor
+// Description:
+//-----------------------------------------------------------------------------
+int uni_set_requestor(vmeRequesterCfg_t * vmeReq)
+{
+	int temp_ctl = 0;
+
+	temp_ctl = readl(vmechip_baseaddr + MAST_CTL);
+	temp_ctl &= 0xFF0FFFFF;
+
+	if (vmeReq->releaseMode == 1) {
+		temp_ctl |= (1 << 20);
+	}
+
+	if (vmeReq->fairMode == 1) {
+		temp_ctl |= (1 << 21);
+	}
+
+	temp_ctl |= (vmeReq->requestLevel << 22);
+
+	writel(temp_ctl, vmechip_baseaddr + MAST_CTL);
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_get_requestor
+// Description:
+//-----------------------------------------------------------------------------
+int uni_get_requestor(vmeRequesterCfg_t * vmeReq)
+{
+	int temp_ctl = 0;
+
+	temp_ctl = readl(vmechip_baseaddr + MAST_CTL);
+
+	if (temp_ctl & (1 << 20)) {
+		vmeReq->releaseMode = 1;
+	}
+
+	if (temp_ctl & (1 << 21)) {
+		vmeReq->fairMode = 1;
+	}
+
+	vmeReq->requestLevel = (temp_ctl & 0xC00000) >> 22;
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_set_in_bound
+// Description:
+//-----------------------------------------------------------------------------
+int uni_set_in_bound(vmeInWindowCfg_t * vmeIn)
+{
+	int temp_ctl = 0;
+
+	// Verify input data
+	if (vmeIn->windowNbr > 7) {
+		return (-EINVAL);
+	}
+	if ((vmeIn->vmeAddrU) || (vmeIn->windowSizeU) || (vmeIn->pciAddrU)) {
+		return (-EINVAL);
+	}
+	if ((vmeIn->vmeAddrL & 0xFFF) ||
+	    (vmeIn->windowSizeL & 0xFFF) || (vmeIn->pciAddrL & 0xFFF)) {
+		return (-EINVAL);
+	}
+
+	if (vmeIn->bcastRespond2esst) {
+		return (-EINVAL);
+	}
+	switch (vmeIn->addrSpace) {
+	case VME_A64:
+	case VME_CRCSR:
+	case VME_USER3:
+	case VME_USER4:
+		return (-EINVAL);
+	case VME_A16:
+		temp_ctl |= 0x00000;
+		break;
+	case VME_A24:
+		temp_ctl |= 0x10000;
+		break;
+	case VME_A32:
+		temp_ctl |= 0x20000;
+		break;
+	case VME_USER1:
+		temp_ctl |= 0x60000;
+		break;
+	case VME_USER2:
+		temp_ctl |= 0x70000;
+		break;
+	}
+
+	// Disable while we are mucking around
+	writel(0x00000000, vmechip_baseaddr + inCTL[vmeIn->windowNbr]);
+	writel(vmeIn->vmeAddrL, vmechip_baseaddr + inBS[vmeIn->windowNbr]);
+	writel(vmeIn->vmeAddrL + vmeIn->windowSizeL,
+	       vmechip_baseaddr + inBD[vmeIn->windowNbr]);
+	writel(vmeIn->pciAddrL - vmeIn->vmeAddrL,
+	       vmechip_baseaddr + inTO[vmeIn->windowNbr]);
+
+	// Setup CTL register.
+	if (vmeIn->wrPostEnable)
+		temp_ctl |= 0x40000000;
+	if (vmeIn->prefetchEnable)
+		temp_ctl |= 0x20000000;
+	if (vmeIn->rmwLock)
+		temp_ctl |= 0x00000040;
+	if (vmeIn->data64BitCapable)
+		temp_ctl |= 0x00000080;
+	if (vmeIn->userAccessType & VME_USER)
+		temp_ctl |= 0x00100000;
+	if (vmeIn->userAccessType & VME_SUPER)
+		temp_ctl |= 0x00200000;
+	if (vmeIn->dataAccessType & VME_DATA)
+		temp_ctl |= 0x00400000;
+	if (vmeIn->dataAccessType & VME_PROG)
+		temp_ctl |= 0x00800000;
+
+	// Write ctl reg without enable
+	writel(temp_ctl, vmechip_baseaddr + inCTL[vmeIn->windowNbr]);
+
+	if (vmeIn->windowEnable)
+		temp_ctl |= 0x80000000;
+
+	writel(temp_ctl, vmechip_baseaddr + inCTL[vmeIn->windowNbr]);
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_get_in_bound
+// Description:
+//-----------------------------------------------------------------------------
+int uni_get_in_bound(vmeInWindowCfg_t * vmeIn)
+{
+	int temp_ctl = 0;
+
+	// Verify input data
+	if (vmeIn->windowNbr > 7) {
+		return (-EINVAL);
+	}
+	// Get Window mappings.
+	vmeIn->vmeAddrL = readl(vmechip_baseaddr + inBS[vmeIn->windowNbr]);
+	vmeIn->pciAddrL = vmeIn->vmeAddrL +
+	    readl(vmechip_baseaddr + inTO[vmeIn->windowNbr]);
+	vmeIn->windowSizeL = readl(vmechip_baseaddr + inBD[vmeIn->windowNbr]) -
+	    vmeIn->vmeAddrL;
+
+	temp_ctl = readl(vmechip_baseaddr + inCTL[vmeIn->windowNbr]);
+
+	// Get Control & BUS attributes
+	if (temp_ctl & 0x40000000)
+		vmeIn->wrPostEnable = 1;
+	if (temp_ctl & 0x20000000)
+		vmeIn->prefetchEnable = 1;
+	if (temp_ctl & 0x00000040)
+		vmeIn->rmwLock = 1;
+	if (temp_ctl & 0x00000080)
+		vmeIn->data64BitCapable = 1;
+	if (temp_ctl & 0x00100000)
+		vmeIn->userAccessType |= VME_USER;
+	if (temp_ctl & 0x00200000)
+		vmeIn->userAccessType |= VME_SUPER;
+	if (temp_ctl & 0x00400000)
+		vmeIn->dataAccessType |= VME_DATA;
+	if (temp_ctl & 0x00800000)
+		vmeIn->dataAccessType |= VME_PROG;
+	if (temp_ctl & 0x80000000)
+		vmeIn->windowEnable = 1;
+
+	switch ((temp_ctl & 0x70000) >> 16) {
+	case 0x0:
+		vmeIn->addrSpace = VME_A16;
+		break;
+	case 0x1:
+		vmeIn->addrSpace = VME_A24;
+		break;
+	case 0x2:
+		vmeIn->addrSpace = VME_A32;
+		break;
+	case 0x6:
+		vmeIn->addrSpace = VME_USER1;
+		break;
+	case 0x7:
+		vmeIn->addrSpace = VME_USER2;
+		break;
+	}
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_set_out_bound
+// Description:
+//-----------------------------------------------------------------------------
+int uni_set_out_bound(vmeOutWindowCfg_t * vmeOut)
+{
+	int temp_ctl = 0;
+
+	// Verify input data
+	if (vmeOut->windowNbr > 7) {
+		return (-EINVAL);
+	}
+	if ((vmeOut->xlatedAddrU) || (vmeOut->windowSizeU)
+	    || (vmeOut->pciBusAddrU)) {
+		return (-EINVAL);
+	}
+	if ((vmeOut->xlatedAddrL & 0xFFF) ||
+	    (vmeOut->windowSizeL & 0xFFF) || (vmeOut->pciBusAddrL & 0xFFF)) {
+		return (-EINVAL);
+	}
+	if (vmeOut->bcastSelect2esst) {
+		return (-EINVAL);
+	}
+	switch (vmeOut->addrSpace) {
+	case VME_A64:
+	case VME_USER3:
+	case VME_USER4:
+		return (-EINVAL);
+	case VME_A16:
+		temp_ctl |= 0x00000;
+		break;
+	case VME_A24:
+		temp_ctl |= 0x10000;
+		break;
+	case VME_A32:
+		temp_ctl |= 0x20000;
+		break;
+	case VME_CRCSR:
+		temp_ctl |= 0x50000;
+		break;
+	case VME_USER1:
+		temp_ctl |= 0x60000;
+		break;
+	case VME_USER2:
+		temp_ctl |= 0x70000;
+		break;
+	}
+
+	// Disable while we are mucking around
+	writel(0x00000000, vmechip_baseaddr + outCTL[vmeOut->windowNbr]);
+	writel(vmeOut->pciBusAddrL,
+	       vmechip_baseaddr + outBS[vmeOut->windowNbr]);
+	writel(vmeOut->pciBusAddrL + vmeOut->windowSizeL,
+	       vmechip_baseaddr + outBD[vmeOut->windowNbr]);
+	writel(vmeOut->xlatedAddrL - vmeOut->pciBusAddrL,
+	       vmechip_baseaddr + outTO[vmeOut->windowNbr]);
+
+	// Sanity check.
+	if (vmeOut->pciBusAddrL !=
+	    readl(vmechip_baseaddr + outBS[vmeOut->windowNbr])) {
+		printk(KERN_ERR
+		       "ca91c042: out window: %x, failed to configure\n",
+		       vmeOut->windowNbr);
+		return (-EINVAL);
+	}
+
+	if (vmeOut->pciBusAddrL + vmeOut->windowSizeL !=
+	    readl(vmechip_baseaddr + outBD[vmeOut->windowNbr])) {
+		printk(KERN_ERR
+		       "ca91c042: out window: %x, failed to configure\n",
+		       vmeOut->windowNbr);
+		return (-EINVAL);
+	}
+
+	if (vmeOut->xlatedAddrL - vmeOut->pciBusAddrL !=
+	    readl(vmechip_baseaddr + outTO[vmeOut->windowNbr])) {
+		printk(KERN_ERR
+		       "ca91c042: out window: %x, failed to configure\n",
+		       vmeOut->windowNbr);
+		return (-EINVAL);
+	}
+	// Setup CTL register.
+	if (vmeOut->wrPostEnable)
+		temp_ctl |= 0x40000000;
+	if (vmeOut->userAccessType & VME_SUPER)
+		temp_ctl |= 0x001000;
+	if (vmeOut->dataAccessType & VME_PROG)
+		temp_ctl |= 0x004000;
+	if (vmeOut->maxDataWidth == VME_D16)
+		temp_ctl |= 0x00400000;
+	if (vmeOut->maxDataWidth == VME_D32)
+		temp_ctl |= 0x00800000;
+	if (vmeOut->maxDataWidth == VME_D64)
+		temp_ctl |= 0x00C00000;
+	if (vmeOut->xferProtocol & (VME_BLT | VME_MBLT))
+		temp_ctl |= 0x00000100;
+
+	// Write ctl reg without enable
+	writel(temp_ctl, vmechip_baseaddr + outCTL[vmeOut->windowNbr]);
+
+	if (vmeOut->windowEnable)
+		temp_ctl |= 0x80000000;
+
+	writel(temp_ctl, vmechip_baseaddr + outCTL[vmeOut->windowNbr]);
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_get_out_bound
+// Description:
+//-----------------------------------------------------------------------------
+int uni_get_out_bound(vmeOutWindowCfg_t * vmeOut)
+{
+	int temp_ctl = 0;
+
+	// Verify input data
+	if (vmeOut->windowNbr > 7) {
+		return (-EINVAL);
+	}
+	// Get Window mappings.
+	vmeOut->pciBusAddrL =
+	    readl(vmechip_baseaddr + outBS[vmeOut->windowNbr]);
+	vmeOut->xlatedAddrL =
+	    vmeOut->pciBusAddrL + readl(vmechip_baseaddr +
+					outTO[vmeOut->windowNbr]);
+	vmeOut->windowSizeL =
+	    readl(vmechip_baseaddr + outBD[vmeOut->windowNbr]) -
+	    vmeOut->pciBusAddrL;
+
+	temp_ctl = readl(vmechip_baseaddr + outCTL[vmeOut->windowNbr]);
+
+	// Get Control & BUS attributes
+	if (temp_ctl & 0x40000000)
+		vmeOut->wrPostEnable = 1;
+	if (temp_ctl & 0x001000)
+		vmeOut->userAccessType = VME_SUPER;
+	else
+		vmeOut->userAccessType = VME_USER;
+	if (temp_ctl & 0x004000)
+		vmeOut->dataAccessType = VME_PROG;
+	else
+		vmeOut->dataAccessType = VME_DATA;
+	if (temp_ctl & 0x80000000)
+		vmeOut->windowEnable = 1;
+
+	switch ((temp_ctl & 0x00C00000) >> 22) {
+	case 0:
+		vmeOut->maxDataWidth = VME_D8;
+		break;
+	case 1:
+		vmeOut->maxDataWidth = VME_D16;
+		break;
+	case 2:
+		vmeOut->maxDataWidth = VME_D32;
+		break;
+	case 3:
+		vmeOut->maxDataWidth = VME_D64;
+		break;
+	}
+	if (temp_ctl & 0x00000100)
+		vmeOut->xferProtocol = VME_BLT;
+	else
+		vmeOut->xferProtocol = VME_SCT;
+
+	switch ((temp_ctl & 0x70000) >> 16) {
+	case 0x0:
+		vmeOut->addrSpace = VME_A16;
+		break;
+	case 0x1:
+		vmeOut->addrSpace = VME_A24;
+		break;
+	case 0x2:
+		vmeOut->addrSpace = VME_A32;
+		break;
+	case 0x5:
+		vmeOut->addrSpace = VME_CRCSR;
+		break;
+	case 0x6:
+		vmeOut->addrSpace = VME_USER1;
+		break;
+	case 0x7:
+		vmeOut->addrSpace = VME_USER2;
+		break;
+	}
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_setup_lm
+// Description:
+//-----------------------------------------------------------------------------
+int uni_setup_lm(vmeLmCfg_t * vmeLm)
+{
+	int temp_ctl = 0;
+
+	if (vmeLm->addrU) {
+		return (-EINVAL);
+	}
+	switch (vmeLm->addrSpace) {
+	case VME_A64:
+	case VME_USER3:
+	case VME_USER4:
+		return (-EINVAL);
+	case VME_A16:
+		temp_ctl |= 0x00000;
+		break;
+	case VME_A24:
+		temp_ctl |= 0x10000;
+		break;
+	case VME_A32:
+		temp_ctl |= 0x20000;
+		break;
+	case VME_CRCSR:
+		temp_ctl |= 0x50000;
+		break;
+	case VME_USER1:
+		temp_ctl |= 0x60000;
+		break;
+	case VME_USER2:
+		temp_ctl |= 0x70000;
+		break;
+	}
+
+	// Disable while we are mucking around
+	writel(0x00000000, vmechip_baseaddr + LM_CTL);
+
+	writel(vmeLm->addr, vmechip_baseaddr + LM_BS);
+
+	// Setup CTL register.
+	if (vmeLm->userAccessType & VME_SUPER)
+		temp_ctl |= 0x00200000;
+	if (vmeLm->userAccessType & VME_USER)
+		temp_ctl |= 0x00100000;
+	if (vmeLm->dataAccessType & VME_PROG)
+		temp_ctl |= 0x00800000;
+	if (vmeLm->dataAccessType & VME_DATA)
+		temp_ctl |= 0x00400000;
+
+	uni_lm_event = 0;
+
+	// Write ctl reg and enable
+	writel(0x80000000 | temp_ctl, vmechip_baseaddr + LM_CTL);
+	temp_ctl = readl(vmechip_baseaddr + LM_CTL);
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_wait_lm
+// Description:
+//-----------------------------------------------------------------------------
+int uni_wait_lm(vmeLmCfg_t * vmeLm)
+{
+	unsigned long flags;
+	unsigned int tmp;
+
+	spin_lock_irqsave(&lm_lock, flags);
+	tmp = uni_lm_event;
+	spin_unlock_irqrestore(&lm_lock, flags);
+	if (tmp == 0) {
+		if (vmeLm->lmWait < 10)
+			vmeLm->lmWait = 10;
+		interruptible_sleep_on_timeout(&lm_queue, vmeLm->lmWait);
+	}
+	writel(0x00000000, vmechip_baseaddr + LM_CTL);
+	vmeLm->lmEvents = uni_lm_event;
+
+	return (0);
+}
+
+#define	SWIZZLE(X) ( ((X & 0xFF000000) >> 24) | ((X & 0x00FF0000) >>  8) | ((X & 0x0000FF00) <<  8) | ((X & 0x000000FF) << 24))
+
+//-----------------------------------------------------------------------------
+// Function   : uni_do_rmw
+// Description:
+//-----------------------------------------------------------------------------
+int uni_do_rmw(vmeRmwCfg_t * vmeRmw)
+{
+	int temp_ctl = 0;
+	int tempBS = 0;
+	int tempBD = 0;
+	int tempTO = 0;
+	int vmeBS = 0;
+	int vmeBD = 0;
+	int *rmw_pci_data_ptr = NULL;
+	int *vaDataPtr = NULL;
+	int i;
+	vmeOutWindowCfg_t vmeOut;
+	if (vmeRmw->maxAttempts < 1) {
+		return (-EINVAL);
+	}
+	if (vmeRmw->targetAddrU) {
+		return (-EINVAL);
+	}
+	// Find the PCI address that maps to the desired VME address
+	for (i = 0; i < 8; i++) {
+		temp_ctl = readl(vmechip_baseaddr + outCTL[i]);
+		if ((temp_ctl & 0x80000000) == 0) {
+			continue;
+		}
+		memset(&vmeOut, 0, sizeof(vmeOut));
+		vmeOut.windowNbr = i;
+		uni_get_out_bound(&vmeOut);
+		if (vmeOut.addrSpace != vmeRmw->addrSpace) {
+			continue;
+		}
+		tempBS = readl(vmechip_baseaddr + outBS[i]);
+		tempBD = readl(vmechip_baseaddr + outBD[i]);
+		tempTO = readl(vmechip_baseaddr + outTO[i]);
+		vmeBS = tempBS + tempTO;
+		vmeBD = tempBD + tempTO;
+		if ((vmeRmw->targetAddr >= vmeBS) &&
+		    (vmeRmw->targetAddr < vmeBD)) {
+			rmw_pci_data_ptr =
+			    (int *)(tempBS + (vmeRmw->targetAddr - vmeBS));
+			vaDataPtr =
+			    (int *)(out_image_va[i] +
+				    (vmeRmw->targetAddr - vmeBS));
+			break;
+		}
+	}
+
+	// If no window - fail.
+	if (rmw_pci_data_ptr == NULL) {
+		return (-EINVAL);
+	}
+	// Setup the RMW registers.
+	writel(0, vmechip_baseaddr + SCYC_CTL);
+	writel(SWIZZLE(vmeRmw->enableMask), vmechip_baseaddr + SCYC_EN);
+	writel(SWIZZLE(vmeRmw->compareData), vmechip_baseaddr + SCYC_CMP);
+	writel(SWIZZLE(vmeRmw->swapData), vmechip_baseaddr + SCYC_SWP);
+	writel((int)rmw_pci_data_ptr, vmechip_baseaddr + SCYC_ADDR);
+	writel(1, vmechip_baseaddr + SCYC_CTL);
+
+	// Run the RMW cycle until either success or max attempts.
+	vmeRmw->numAttempts = 1;
+	while (vmeRmw->numAttempts <= vmeRmw->maxAttempts) {
+
+		if ((readl(vaDataPtr) & vmeRmw->enableMask) ==
+		    (vmeRmw->swapData & vmeRmw->enableMask)) {
+
+			writel(0, vmechip_baseaddr + SCYC_CTL);
+			break;
+
+		}
+		vmeRmw->numAttempts++;
+	}
+
+	// If no success, set num Attempts to be greater than max attempts
+	if (vmeRmw->numAttempts > vmeRmw->maxAttempts) {
+		vmeRmw->numAttempts = vmeRmw->maxAttempts + 1;
+	}
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uniSetupDctlReg
+// Description:
+//-----------------------------------------------------------------------------
+int uniSetupDctlReg(vmeDmaPacket_t * vmeDma, int *dctlregreturn)
+{
+	unsigned int dctlreg = 0x80;
+	struct vmeAttr *vmeAttr;
+
+	if (vmeDma->srcBus == VME_DMA_VME) {
+		dctlreg = 0;
+		vmeAttr = &vmeDma->srcVmeAttr;
+	} else {
+		dctlreg = 0x80000000;
+		vmeAttr = &vmeDma->dstVmeAttr;
+	}
+
+	switch (vmeAttr->maxDataWidth) {
+	case VME_D8:
+		break;
+	case VME_D16:
+		dctlreg |= 0x00400000;
+		break;
+	case VME_D32:
+		dctlreg |= 0x00800000;
+		break;
+	case VME_D64:
+		dctlreg |= 0x00C00000;
+		break;
+	}
+
+	switch (vmeAttr->addrSpace) {
+	case VME_A16:
+		break;
+	case VME_A24:
+		dctlreg |= 0x00010000;
+		break;
+	case VME_A32:
+		dctlreg |= 0x00020000;
+		break;
+	case VME_USER1:
+		dctlreg |= 0x00060000;
+		break;
+	case VME_USER2:
+		dctlreg |= 0x00070000;
+		break;
+
+	case VME_A64:		// not supported in Universe DMA
+	case VME_CRCSR:
+	case VME_USER3:
+	case VME_USER4:
+		return (-EINVAL);
+		break;
+	}
+	if (vmeAttr->userAccessType == VME_PROG) {
+		dctlreg |= 0x00004000;
+	}
+	if (vmeAttr->dataAccessType == VME_SUPER) {
+		dctlreg |= 0x00001000;
+	}
+	if (vmeAttr->xferProtocol != VME_SCT) {
+		dctlreg |= 0x00000100;
+	}
+	*dctlregreturn = dctlreg;
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_start_dma
+// Description:
+//-----------------------------------------------------------------------------
+unsigned int
+uni_start_dma(int channel, unsigned int dgcsreg, TDMA_Cmd_Packet * vmeLL)
+{
+	unsigned int val;
+
+	// Setup registers as needed for direct or chained.
+	if (dgcsreg & 0x8000000) {
+		writel(0, vmechip_baseaddr + DTBC);
+		writel((unsigned int)vmeLL, vmechip_baseaddr + DCPP);
+	} else {
+#if	0
+		printk("Starting: DGCS = %08x\n", dgcsreg);
+		printk("Starting: DVA  = %08x\n", readl(&vmeLL->dva));
+		printk("Starting: DLV  = %08x\n", readl(&vmeLL->dlv));
+		printk("Starting: DTBC = %08x\n", readl(&vmeLL->dtbc));
+		printk("Starting: DCTL = %08x\n", readl(&vmeLL->dctl));
+#endif
+		// Write registers
+		writel(readl(&vmeLL->dva), vmechip_baseaddr + DVA);
+		writel(readl(&vmeLL->dlv), vmechip_baseaddr + DLA);
+		writel(readl(&vmeLL->dtbc), vmechip_baseaddr + DTBC);
+		writel(readl(&vmeLL->dctl), vmechip_baseaddr + DCTL);
+		writel(0, vmechip_baseaddr + DCPP);
+	}
+
+	// Start the operation
+	writel(dgcsreg, vmechip_baseaddr + DGCS);
+	val = get_tbl();
+	writel(dgcsreg | 0x8000000F, vmechip_baseaddr + DGCS);
+	return (val);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_setup_dma
+// Description:
+//-----------------------------------------------------------------------------
+TDMA_Cmd_Packet *uni_setup_dma(vmeDmaPacket_t * vmeDma)
+{
+	vmeDmaPacket_t *vmeCur;
+	int maxPerPage;
+	int currentLLcount;
+	TDMA_Cmd_Packet *startLL;
+	TDMA_Cmd_Packet *currentLL;
+	TDMA_Cmd_Packet *nextLL;
+	unsigned int dctlreg = 0;
+
+	maxPerPage = PAGESIZE / sizeof(TDMA_Cmd_Packet) - 1;
+	startLL = (TDMA_Cmd_Packet *) __get_free_pages(GFP_KERNEL, 0);
+	if (startLL == 0) {
+		return (startLL);
+	}
+	// First allocate pages for descriptors and create linked list
+	vmeCur = vmeDma;
+	currentLL = startLL;
+	currentLLcount = 0;
+	while (vmeCur != 0) {
+		if (vmeCur->pNextPacket != 0) {
+			currentLL->dcpp = (unsigned int)(currentLL + 1);
+			currentLLcount++;
+			if (currentLLcount >= maxPerPage) {
+				currentLL->dcpp =
+				    __get_free_pages(GFP_KERNEL, 0);
+				currentLLcount = 0;
+			}
+			currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp;
+		} else {
+			currentLL->dcpp = (unsigned int)0;
+		}
+		vmeCur = vmeCur->pNextPacket;
+	}
+
+	// Next fill in information for each descriptor
+	vmeCur = vmeDma;
+	currentLL = startLL;
+	while (vmeCur != 0) {
+		if (vmeCur->srcBus == VME_DMA_VME) {
+			writel(vmeCur->srcAddr, &currentLL->dva);
+			writel(vmeCur->dstAddr, &currentLL->dlv);
+		} else {
+			writel(vmeCur->srcAddr, &currentLL->dlv);
+			writel(vmeCur->dstAddr, &currentLL->dva);
+		}
+		uniSetupDctlReg(vmeCur, &dctlreg);
+		writel(dctlreg, &currentLL->dctl);
+		writel(vmeCur->byteCount, &currentLL->dtbc);
+
+		currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp;
+		vmeCur = vmeCur->pNextPacket;
+	}
+
+	// Convert Links to PCI addresses.
+	currentLL = startLL;
+	while (currentLL != 0) {
+		nextLL = (TDMA_Cmd_Packet *) currentLL->dcpp;
+		if (nextLL == 0) {
+			writel(1, &currentLL->dcpp);
+		} else {
+			writel((unsigned int)virt_to_bus(nextLL),
+			       &currentLL->dcpp);
+		}
+		currentLL = nextLL;
+	}
+
+	// Return pointer to descriptors list
+	return (startLL);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_free_dma
+// Description:
+//-----------------------------------------------------------------------------
+int uni_free_dma(TDMA_Cmd_Packet * startLL)
+{
+	TDMA_Cmd_Packet *currentLL;
+	TDMA_Cmd_Packet *prevLL;
+	TDMA_Cmd_Packet *nextLL;
+	unsigned int dcppreg;
+
+	// Convert Links to virtual addresses.
+	currentLL = startLL;
+	while (currentLL != 0) {
+		dcppreg = readl(&currentLL->dcpp);
+		dcppreg &= ~6;
+		if (dcppreg & 1) {
+			currentLL->dcpp = 0;
+		} else {
+			currentLL->dcpp = (unsigned int)bus_to_virt(dcppreg);
+		}
+		currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp;
+	}
+
+	// Free all pages associated with the descriptors.
+	currentLL = startLL;
+	prevLL = currentLL;
+	while (currentLL != 0) {
+		nextLL = (TDMA_Cmd_Packet *) currentLL->dcpp;
+		if (currentLL + 1 != nextLL) {
+			free_pages((int)prevLL, 0);
+			prevLL = nextLL;
+		}
+		currentLL = nextLL;
+	}
+
+	// Return pointer to descriptors list
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_do_dma
+// Description:
+//-----------------------------------------------------------------------------
+int uni_do_dma(vmeDmaPacket_t * vmeDma)
+{
+	unsigned int dgcsreg = 0;
+	unsigned int dctlreg = 0;
+	int val;
+	int channel, x;
+	vmeDmaPacket_t *curDma;
+	TDMA_Cmd_Packet *dmaLL;
+
+	// Sanity check the VME chain.
+	channel = vmeDma->channel_number;
+	if (channel > 0) {
+		return (-EINVAL);
+	}
+	curDma = vmeDma;
+	while (curDma != 0) {
+		if (curDma->byteCount == 0) {
+			return (-EINVAL);
+		}
+		if (curDma->byteCount >= 0x1000000) {
+			return (-EINVAL);
+		}
+		if ((curDma->srcAddr & 7) != (curDma->dstAddr & 7)) {
+			return (-EINVAL);
+		}
+		switch (curDma->srcBus) {
+		case VME_DMA_PCI:
+			if (curDma->dstBus != VME_DMA_VME) {
+				return (-EINVAL);
+			}
+			break;
+		case VME_DMA_VME:
+			if (curDma->dstBus != VME_DMA_PCI) {
+				return (-EINVAL);
+			}
+			break;
+		default:
+			return (-EINVAL);
+			break;
+		}
+		if (uniSetupDctlReg(curDma, &dctlreg) < 0) {
+			return (-EINVAL);
+		}
+
+		curDma = curDma->pNextPacket;
+		if (curDma == vmeDma) {	// Endless Loop!
+			return (-EINVAL);
+		}
+	}
+
+	// calculate control register
+	if (vmeDma->pNextPacket != 0) {
+		dgcsreg = 0x8000000;
+	} else {
+		dgcsreg = 0;
+	}
+
+	for (x = 0; x < 8; x++) {	// vme block size
+		if ((256 << x) >= vmeDma->maxVmeBlockSize) {
+			break;
+		}
+	}
+	if (x == 8)
+		x = 7;
+	dgcsreg |= (x << 20);
+
+	if (vmeDma->vmeBackOffTimer) {
+		for (x = 1; x < 8; x++) {	// vme timer
+			if ((16 << (x - 1)) >= vmeDma->vmeBackOffTimer) {
+				break;
+			}
+		}
+		if (x == 8)
+			x = 7;
+		dgcsreg |= (x << 16);
+	}
+	// Setup the dma chain
+	dmaLL = uni_setup_dma(vmeDma);
+
+	// Start the DMA
+	if (dgcsreg & 0x8000000) {
+		vmeDma->vmeDmaStartTick =
+		    uni_start_dma(channel, dgcsreg,
+				  (TDMA_Cmd_Packet *) virt_to_phys(dmaLL));
+	} else {
+		vmeDma->vmeDmaStartTick =
+		    uni_start_dma(channel, dgcsreg, dmaLL);
+	}
+
+	wait_event_interruptible(dma_queue[0],
+				 readl(vmechip_baseaddr + DGCS) & 0x800);
+
+	val = readl(vmechip_baseaddr + DGCS);
+	writel(val | 0xF00, vmechip_baseaddr + DGCS);
+
+	vmeDma->vmeDmaStatus = 0;
+	vmeDma->vmeDmaStopTick = uni_dma_irq_time;
+	if (vmeDma->vmeDmaStopTick < vmeDma->vmeDmaStartTick) {
+		vmeDma->vmeDmaElapsedTime =
+		    (0xFFFFFFFF - vmeDma->vmeDmaStartTick) +
+		    vmeDma->vmeDmaStopTick;
+	} else {
+		vmeDma->vmeDmaElapsedTime =
+		    vmeDma->vmeDmaStopTick - vmeDma->vmeDmaStartTick;
+	}
+	vmeDma->vmeDmaElapsedTime -= vmechip_irq_overhead_ticks;
+	vmeDma->vmeDmaElapsedTime /= (tb_speed / 1000000);
+
+	if (!(val & 0x00000800)) {
+		vmeDma->vmeDmaStatus = val & 0x700;
+		printk(KERN_ERR
+		       "ca91c042: DMA Error in DMA_uni_irqhandler DGCS=%08X\n",
+		       val);
+		val = readl(vmechip_baseaddr + DCPP);
+		printk(KERN_ERR "ca91c042: DCPP=%08X\n", val);
+		val = readl(vmechip_baseaddr + DCTL);
+		printk(KERN_ERR "ca91c042: DCTL=%08X\n", val);
+		val = readl(vmechip_baseaddr + DTBC);
+		printk(KERN_ERR "ca91c042: DTBC=%08X\n", val);
+		val = readl(vmechip_baseaddr + DLA);
+		printk(KERN_ERR "ca91c042: DLA=%08X\n", val);
+		val = readl(vmechip_baseaddr + DVA);
+		printk(KERN_ERR "ca91c042: DVA=%08X\n", val);
+
+	}
+	// Free the dma chain
+	uni_free_dma(dmaLL);
+
+	return (0);
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_shutdown
+// Description: Put VME bridge in quiescent state.
+//-----------------------------------------------------------------------------
+void uni_shutdown(void)
+{
+	writel(0, vmechip_baseaddr + LINT_EN);	// Turn off Ints
+
+	// Turn off the windows
+	writel(0x00800000, vmechip_baseaddr + LSI0_CTL);
+	writel(0x00800000, vmechip_baseaddr + LSI1_CTL);
+	writel(0x00800000, vmechip_baseaddr + LSI2_CTL);
+	writel(0x00800000, vmechip_baseaddr + LSI3_CTL);
+	writel(0x00F00000, vmechip_baseaddr + VSI0_CTL);
+	writel(0x00F00000, vmechip_baseaddr + VSI1_CTL);
+	writel(0x00F00000, vmechip_baseaddr + VSI2_CTL);
+	writel(0x00F00000, vmechip_baseaddr + VSI3_CTL);
+	if (vmechip_revision >= 2) {
+		writel(0x00800000, vmechip_baseaddr + LSI4_CTL);
+		writel(0x00800000, vmechip_baseaddr + LSI5_CTL);
+		writel(0x00800000, vmechip_baseaddr + LSI6_CTL);
+		writel(0x00800000, vmechip_baseaddr + LSI7_CTL);
+		writel(0x00F00000, vmechip_baseaddr + VSI4_CTL);
+		writel(0x00F00000, vmechip_baseaddr + VSI5_CTL);
+		writel(0x00F00000, vmechip_baseaddr + VSI6_CTL);
+		writel(0x00F00000, vmechip_baseaddr + VSI7_CTL);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Function   : uni_init()
+// Description:
+//-----------------------------------------------------------------------------
+int uni_init(void)
+{
+	int result;
+	unsigned int tmp;
+	unsigned int crcsr_addr;
+	unsigned int irqOverHeadStart;
+	int overHeadTicks;
+
+	uni_shutdown();
+
+	// Write to Misc Register
+	// Set VME Bus Time-out
+	//   Arbitration Mode
+	//   DTACK Enable
+	tmp = readl(vmechip_baseaddr + MISC_CTL) & 0x0832BFFF;
+	tmp |= 0x76040000;
+	writel(tmp, vmechip_baseaddr + MISC_CTL);
+	if (tmp & 0x20000) {
+		vme_syscon = 1;
+	} else {
+		vme_syscon = 0;
+	}
+
+	// Clear DMA status log
+	writel(0x00000F00, vmechip_baseaddr + DGCS);
+	// Clear and enable error log
+	writel(0x00800000, vmechip_baseaddr + L_CMDERR);
+	// Turn off location monitor
+	writel(0x00000000, vmechip_baseaddr + LM_CTL);
+
+	// Initialize crcsr map
+	if (vme_slotnum != -1) {
+		writel(vme_slotnum << 27, vmechip_baseaddr + VCSR_BS);
+	}
+	crcsr_addr = readl(vmechip_baseaddr + VCSR_BS) >> 8;
+	writel((unsigned int)vmechip_interboard_datap - crcsr_addr,
+	       vmechip_baseaddr + VCSR_TO);
+	if (vme_slotnum != -1) {
+		writel(0x80000000, vmechip_baseaddr + VCSR_CTL);
+	}
+	// Turn off interrupts
+	writel(0x00000000, vmechip_baseaddr + LINT_EN);	// Disable interrupts in the Universe first
+	writel(0x00FFFFFF, vmechip_baseaddr + LINT_STAT);	// Clear Any Pending Interrupts
+	writel(0x00000000, vmechip_baseaddr + VINT_EN);	// Disable interrupts in the Universe first
+
+	result =
+	    request_irq(vmechip_irq, uni_irqhandler, IRQF_SHARED | IRQF_DISABLED,
+			"VMEBus (ca91c042)", vmechip_baseaddr);
+	if (result) {
+		printk(KERN_ERR
+		       "ca91c042: can't get assigned pci irq vector %02X\n",
+		       vmechip_irq);
+		return (0);
+	} else {
+		writel(0x0000, vmechip_baseaddr + LINT_MAP0);	// Map all ints to 0
+		writel(0x0000, vmechip_baseaddr + LINT_MAP1);	// Map all ints to 0
+		writel(0x0000, vmechip_baseaddr + LINT_MAP2);	// Map all ints to 0
+	}
+
+	// Enable DMA, mailbox, VIRQ & LM Interrupts
+	if (vme_syscon)
+		tmp = 0x00FF07FE;
+	else
+		tmp = 0x00FF0700;
+	writel(tmp, vmechip_baseaddr + LINT_EN);
+
+	// Do a quick sanity test of the bridge
+	if (readl(vmechip_baseaddr + LINT_EN) != tmp) {
+		return (0);
+	}
+	if (readl(vmechip_baseaddr + PCI_CLASS_REVISION) != 0x06800002) {
+		return (0);
+	}
+	for (tmp = 1; tmp < 0x80000000; tmp = tmp << 1) {
+		writel(tmp, vmechip_baseaddr + SCYC_EN);
+		writel(~tmp, vmechip_baseaddr + SCYC_CMP);
+		if (readl(vmechip_baseaddr + SCYC_EN) != tmp) {
+			return (0);
+		}
+		if (readl(vmechip_baseaddr + SCYC_CMP) != ~tmp) {
+			return (0);
+		}
+	}
+
+	// do a mail box interrupt to calibrate the interrupt overhead.
+
+	irqOverHeadStart = get_tbl();
+	writel(0, vmechip_baseaddr + MBOX1);
+	for (tmp = 0; tmp < 10; tmp++) {
+	}
+
+	irqOverHeadStart = get_tbl();
+	writel(0, vmechip_baseaddr + MBOX1);
+	for (tmp = 0; tmp < 10; tmp++) {
+	}
+
+	overHeadTicks = uni_irq_time - irqOverHeadStart;
+	if (overHeadTicks > 0) {
+		vmechip_irq_overhead_ticks = overHeadTicks;
+	} else {
+		vmechip_irq_overhead_ticks = 1;
+	}
+	return (1);
+}
--- /dev/null
+++ b/drivers/staging/vme/bridges/vme_ca91cx42.h
@@ -0,0 +1,403 @@
+/*
+ * ca91c042.h
+ *
+ * Support for the Tundra Universe 1 and Universe II VME bridge chips
+ *
+ * Author: Tom Armistead
+ * Updated and maintained by Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * Derived from ca91c042.h by Michael Wyrick
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _ca91c042_h
+#define _ca91c042_h
+
+#ifndef	PCI_VENDOR_ID_TUNDRA
+#define	PCI_VENDOR_ID_TUNDRA 0x10e3
+#endif
+
+#ifndef	PCI_DEVICE_ID_TUNDRA_CA91C042
+#define	PCI_DEVICE_ID_TUNDRA_CA91C042 0x0000
+#endif
+
+//-----------------------------------------------------------------------------
+// Public Functions
+//-----------------------------------------------------------------------------
+// This is the typedef for a VmeIrqHandler
+typedef void (*TirqHandler) (int vmeirq, int vector, void *dev_id,
+			     struct pt_regs * regs);
+// This is the typedef for a DMA Transfer Callback function
+typedef void (*TDMAcallback) (int status);
+
+//  Returns the PCI baseaddress of the Universe chip
+char *Universe_BaseAddr(void);
+//  Returns the PCI IRQ That the universe is using
+int Universe_IRQ(void);
+
+char *mapvme(unsigned int pci, unsigned int vme, unsigned int size,
+	     int image, int ctl);
+void unmapvme(char *ptr, int image);
+
+// Interrupt Stuff
+void enable_vmeirq(unsigned int irq);
+void disable_vmeirq(unsigned int irq);
+int request_vmeirq(unsigned int irq, TirqHandler);
+void free_vmeirq(unsigned int irq);
+
+// DMA Stuff
+
+int VME_Bus_Error(void);
+int uni_procinfo(char *);
+
+#define IRQ_VOWN    0x0001
+#define IRQ_VIRQ1   0x0002
+#define IRQ_VIRQ2   0x0004
+#define IRQ_VIRQ3   0x0008
+#define IRQ_VIRQ4   0x0010
+#define IRQ_VIRQ5   0x0020
+#define IRQ_VIRQ6   0x0040
+#define IRQ_VIRQ7   0x0080
+#define IRQ_DMA     0x0100
+#define IRQ_LERR    0x0200
+#define IRQ_VERR    0x0400
+#define IRQ_res     0x0800
+#define IRQ_IACK    0x1000
+#define IRQ_SWINT   0x2000
+#define IRQ_SYSFAIL 0x4000
+#define IRQ_ACFAIL  0x8000
+
+// See Page 2-77 in the Universe User Manual
+typedef struct {
+	unsigned int dctl;	// DMA Control
+	unsigned int dtbc;	// Transfer Byte Count
+	unsigned int dlv;	// PCI Address
+	unsigned int res1;	// Reserved
+	unsigned int dva;	// Vme Address
+	unsigned int res2;	// Reserved
+	unsigned int dcpp;	// Pointer to Numed Cmd Packet with rPN
+	unsigned int res3;	// Reserved
+} TDMA_Cmd_Packet;
+
+/*
+ * Below here is normaly not used by a user module
+ */
+#define  DMATIMEOUT 2*HZ;
+
+// Define for the Universe
+#define SEEK_SET 0
+#define SEEK_CUR 1
+
+#define CONFIG_REG_SPACE        0xA0000000
+
+/* Universe Register Offsets */
+/* general PCI configuration registers */
+#define UNIV_PCI_ID             0x000
+#define UNIV_PCI_CSR            0x004
+#define UNIV_PCI_CLASS          0x008
+#define UNIV_BM_PCI_CLASS_BASE          0xFF000000
+#define UNIV_OF_PCI_CLASS_BASE          24
+#define UNIV_BM_PCI_CLASS_SUB           0x00FF0000
+#define UNIV_OF_PCI_CLASS_SUB           16
+#define UNIV_BM_PCI_CLASS_PROG          0x0000FF00
+#define UNIV_OF_PCI_CLASS_PROG          8
+#define UNIV_BM_PCI_CLASS_RID           0x000000FF
+#define UNIV_OF_PCI_CLASS_RID           0
+
+#define UNIV_OF_PCI_CLASS_RID_UNIVERSE_I 0
+#define UNIV_OF_PCI_CLASS_RID_UNIVERSE_II 1
+
+#define UNIV_PCI_MISC0          0x00C
+#define UNIV_BM_PCI_MISC0_BISTC         0x80000000
+#define UNIV_BM_PCI_MISC0_SBIST         0x60000000
+#define UNIV_BM_PCI_MISC0_CCODE         0x0F000000
+#define UNIV_BM_PCI_MISC0_MFUNCT        0x00800000
+#define UNIV_BM_PCI_MISC0_LAYOUT        0x007F0000
+#define UNIV_BM_PCI_MISC0_LTIMER        0x0000FF00
+#define UNIV_OF_PCI_MISC0_LTIMER        8
+#define UNIV_PCI_BS             0x010
+#define UNIV_PCI_MISC1          0x03C
+
+#define UNIV_BM_LSI_CTL_EN      0x80000000
+#define UNIV_BM_LSI_CTL_PWEN    0x40000000
+#define UNIV_BM_LSI_CTL_VDW     0x00C00000
+#define UNIV_OF_LSI_CTL_VDW     22
+#define UNIV_BM_LSI_CTL_VAS     0x00070000
+#define UNIV_OF_LSI_CTL_VAS     16
+#define UNIV_BM_LSI_CTL_PGM     0x0000C000
+#define UNIV_OF_LSI_CTL_PGM     14
+#define UNIV_BM_LSI_CTL_SUPER   0x00003000
+#define UNIV_OF_LSI_CTL_SUPER   12
+#define UNIV_BM_LSI_CTL_VCT     0x00000100
+#define UNIV_BM_LSI_CTL_LAS     0x00000003
+#define UNIV_OF_LSI_CTL_LAS     0
+#define UNIV_BM_LSI_CTL_RESERVED (~ (UNIV_BM_LSI_CTL_EN | UNIV_BM_LSI_CTL_PWEN | UNIV_BM_LSI_CTL_VDW | UNIV_BM_LSI_CTL_VAS | UNIV_BM_LSI_CTL_PGM | UNIV_BM_LSI_CTL_SUPER | UNIV_BM_LSI_CTL_VCT | UNIV_BM_LSI_CTL_LAS))
+
+#define PCI_SIZE_8	    0x0001
+#define PCI_SIZE_16	    0x0002
+#define PCI_SIZE_32	    0x0003
+
+#define IOCTL_SET_CTL 	0xF001
+#define IOCTL_SET_BS	0xF002
+#define IOCTL_SET_BD	0xF003
+#define IOCTL_SET_TO	0xF004
+#define IOCTL_PCI_SIZE  0xF005
+#define IOCTL_SET_MODE 	0xF006
+#define IOCTL_SET_WINT  0xF007	// Wait for interrupt before read
+
+#define LSI0_CTL	0x0100
+#define LSI0_BS		0x0104
+#define LSI0_BD		0x0108
+#define LSI0_TO		0x010C
+
+#define LSI1_CTL	      0x0114
+#define LSI1_BS		      0x0118
+#define LSI1_BD		      0x011C
+#define LSI1_TO		      0x0120
+
+#define LSI2_CTL	      0x0128
+#define LSI2_BS		      0x012C
+#define LSI2_BD		      0x0130
+#define LSI2_TO		      0x0134
+
+#define LSI3_CTL	      0x013C
+#define LSI3_BS		      0x0140
+#define LSI3_BD		      0x0144
+#define LSI3_TO		      0x0148
+
+#define LSI4_CTL	      0x01A0
+#define LSI4_BS		      0x01A4
+#define LSI4_BD		      0x01A8
+#define LSI4_TO		      0x01AC
+
+#define LSI5_CTL	      0x01B4
+#define LSI5_BS		      0x01B8
+#define LSI5_BD		      0x01BC
+#define LSI5_TO		      0x01C0
+
+#define LSI6_CTL	      0x01C8
+#define LSI6_BS		      0x01CC
+#define LSI6_BD		      0x01D0
+#define LSI6_TO		      0x01D4
+
+#define LSI7_CTL	      0x01DC
+#define LSI7_BS		      0x01E0
+#define LSI7_BD		      0x01E4
+#define LSI7_TO		      0x01E8
+
+#define SCYC_CTL		0x0170
+#define SCYC_ADDR		0x0174
+#define SCYC_EN			0x0178
+#define SCYC_CMP		0x017C
+#define SCYC_SWP		0x0180
+#define LMISC			0x0184
+#define UNIV_BM_LMISC_CRT               0xF0000000
+#define UNIV_OF_LMISC_CRT               28
+#define UNIV_BM_LMISC_CWT               0x0F000000
+#define UNIV_OF_LMISC_CWT               24
+#define SLSI		        0x0188
+#define UNIV_BM_SLSI_EN                 0x80000000
+#define UNIV_BM_SLSI_PWEN               0x40000000
+#define UNIV_BM_SLSI_VDW                0x00F00000
+#define UNIV_OF_SLSI_VDW                20
+#define UNIV_BM_SLSI_PGM                0x0000F000
+#define UNIV_OF_SLSI_PGM                12
+#define UNIV_BM_SLSI_SUPER              0x00000F00
+#define UNIV_OF_SLSI_SUPER              8
+#define UNIV_BM_SLSI_BS                 0x000000F6
+#define UNIV_OF_SLSI_BS                 2
+#define UNIV_BM_SLSI_LAS                0x00000003
+#define UNIV_OF_SLSI_LAS                0
+#define UNIV_BM_SLSI_RESERVED           0x3F0F0000
+#define L_CMDERR		0x018C
+#define LAERR		        0x0190
+
+#define DCTL		        0x0200
+#define DTBC		        0x0204
+#define DLA			0x0208
+#define DVA			0x0210
+#define DCPP		        0x0218
+#define DGCS		        0x0220
+#define D_LLUE			0x0224
+
+#define LINT_EN		      0x0300
+#define UNIV_BM_LINT_ACFAIL             0x00008000
+#define UNIV_BM_LINT_SYSFAIL            0x00004000
+#define UNIV_BM_LINT_SW_INT             0x00002000
+#define UNIV_BM_LINT_SW_IACK            0x00001000
+#define UNIV_BM_LINT_VERR               0x00000400
+#define UNIV_BM_LINT_LERR               0x00000200
+#define UNIV_BM_LINT_DMA                0x00000100
+#define UNIV_BM_LINT_LM                 0x00F00000
+#define UNIV_BM_LINT_MBOX               0x000F0000
+#define UNIV_BM_LINT_VIRQ               0x000000FE
+#define UNIV_BM_LINT_VIRQ7              0x00000080
+#define UNIV_BM_LINT_VIRQ6              0x00000040
+#define UNIV_BM_LINT_VIRQ5              0x00000020
+#define UNIV_BM_LINT_VIRQ4              0x00000010
+#define UNIV_BM_LINT_VIRQ3              0x00000008
+#define UNIV_BM_LINT_VIRQ2              0x00000004
+#define UNIV_BM_LINT_VIRQ1              0x00000002
+#define UNIV_BM_LINT_VOWN               0x00000001
+#define LINT_STAT	      0x0304
+#define LINT_MAP0	      0x0308
+#define LINT_MAP1	      0x030C
+#define VINT_EN		      0x0310
+#define VINT_STAT	      0x0314
+#define VINT_MAP0	      0x0318
+#define VINT_MAP1	      0x031C
+#define STATID		      0x0320
+#define V1_STATID	      0x0324
+#define V2_STATID	      0x0328
+#define V3_STATID	      0x032C
+#define V4_STATID	      0x0330
+#define V5_STATID	      0x0334
+#define V6_STATID	      0x0338
+#define V7_STATID	      0x033C
+#define LINT_MAP2	      0x0340
+#define VINT_MAP2	      0x0344
+
+#define MBOX0	              0x0348
+#define MBOX1	              0x034C
+#define MBOX2		      0x0350
+#define MBOX3		      0x0354
+#define SEMA0		      0x0358
+#define SEMA1		      0x035C
+
+#define MAST_CTL	      0x0400
+#define UNIV_BM_MAST_CTL_MAXRTRY        0xF0000000
+#define UNIV_OF_MAST_CTL_MAXRTRY        28
+#define UNIV_BM_MAST_CTL_PWON           0x0F000000
+#define UNIV_OF_MAST_CTL_PWON           24
+#define UNIV_BM_MAST_CTL_VRL            0x00C00000
+#define UNIV_OF_MAST_CTL_VRL            22
+#define UNIV_BM_MAST_CTL_VRM            0x00200000
+#define UNIV_BM_MAST_CTL_VREL           0x00100000
+#define UNIV_BM_MAST_CTL_VOWN           0x00080000
+#define UNIV_BM_MAST_CTL_VOWN_ACK       0x00040000
+#define UNIV_BM_MAST_CTL_PABS           0x00001000
+#define UNIV_BM_MAST_CTL_BUS_NO         0x0000000F
+#define UNIV_OF_MAST_CTL_BUS_NO         0
+
+#define MISC_CTL	      0x0404
+#define UNIV_BM_MISC_CTL_VBTO           0xF0000000
+#define UNIV_OF_MISC_CTL_VBTO           28
+#define UNIV_BM_MISC_CTL_VARB           0x04000000
+#define UNIV_BM_MISC_CTL_VARBTO         0x03000000
+#define UNIV_OF_MISC_CTL_VARBTO         24
+#define UNIV_BM_MISC_CTL_SW_LRST        0x00800000
+#define UNIV_BM_MISC_CTL_SW_SRST        0x00400000
+#define UNIV_BM_MISC_CTL_BI             0x00100000
+#define UNIV_BM_MISC_CTL_ENGBI          0x00080000
+#define UNIV_BM_MISC_CTL_RESCIND        0x00040000
+#define UNIV_BM_MISC_CTL_SYSCON         0x00020000
+#define UNIV_BM_MISC_CTL_V64AUTO        0x00010000
+#define UNIV_BM_MISC_CTL_RESERVED       0x0820FFFF
+
+#define MISC_STAT	      0x0408
+#define UNIV_BM_MISC_STAT_ENDIAN        0x80000000
+#define UNIV_BM_MISC_STAT_LCLSIZE       0x40000000
+#define UNIV_BM_MISC_STAT_DY4AUTO       0x08000000
+#define UNIV_BM_MISC_STAT_MYBBSY        0x00200000
+#define UNIV_BM_MISC_STAT_DY4DONE       0x00080000
+#define UNIV_BM_MISC_STAT_TXFE          0x00040000
+#define UNIV_BM_MISC_STAT_RXFE          0x00020000
+#define UNIV_BM_MISC_STAT_DY4AUTOID     0x0000FF00
+#define UNIV_OF_MISC_STAT_DY4AUTOID     8
+
+#define USER_AM		      0x040C
+
+#define VSI0_CTL	      0x0F00
+#define VSI0_BS		      0x0F04
+#define VSI0_BD		      0x0F08
+#define VSI0_TO		      0x0F0C
+
+#define VSI1_CTL	      0x0F14
+#define VSI1_BS		      0x0F18
+#define VSI1_BD		      0x0F1C
+#define VSI1_TO		      0x0F20
+
+#define VSI2_CTL	      0x0F28
+#define VSI2_BS		      0x0F2C
+#define VSI2_BD		      0x0F30
+#define VSI2_TO		      0x0F34
+
+#define VSI3_CTL	      0x0F3C
+#define VSI3_BS		      0x0F40
+#define VSI3_BD		      0x0F44
+#define VSI3_TO		      0x0F48
+
+#define LM_CTL		      0x0F64
+#define LM_BS		      0x0F68
+
+#define VRAI_CTL	      0x0F70
+#define UNIV_BM_VRAI_CTL_EN             0x80000000
+#define UNIV_BM_VRAI_CTL_PGM            0x00C00000
+#define UNIV_OF_VRAI_CTL_PGM            22
+#define UNIV_BM_VRAI_CTL_SUPER          0x00300000
+#define UNIV_OF_VRAI_CTL_SUPER          20
+#define UNIV_BM_VRAI_CTL_VAS            0x00030000
+#define UNIV_OF_VRAI_CTL_VAS            16
+
+#define VRAI_BS		      0x0F74
+#define VCSR_CTL	      0x0F80
+#define VCSR_TO		      0x0F84
+#define V_AMERR		      0x0F88
+#define VAERR			0x0F8C
+
+#define VSI4_CTL	      0x0F90
+#define VSI4_BS		      0x0F94
+#define VSI4_BD		      0x0F98
+#define VSI4_TO		      0x0F9C
+
+#define VSI5_CTL	      0x0FA4
+#define VSI5_BS		      0x0FA8
+#define VSI5_BD		      0x0FAC
+#define VSI5_TO		      0x0FB0
+
+#define VSI6_CTL	      0x0FB8
+#define VSI6_BS		      0x0FBC
+#define VSI6_BD		      0x0FC0
+#define VSI6_TO		      0x0FC4
+
+#define VSI7_CTL	      0x0FCC
+#define VSI7_BS		      0x0FD0
+#define VSI7_BD		      0x0FD4
+#define VSI7_TO		      0x0FD8
+
+#define VCSR_CLR	      0x0FF4
+#define VCSR_SET	      0x0FF8
+#define VCSR_BS		      0x0FFC
+
+// DMA General Control/Status Register DGCS (0x220)
+// 32-24 ||  GO   | STOPR | HALTR |   0   || CHAIN |   0   |   0   |   0   ||
+// 23-16 ||              VON              ||             VOFF              ||
+// 15-08 ||  ACT  | STOP  | HALT  |   0   || DONE  | LERR  | VERR  | P_ERR ||
+// 07-00 ||   0   | INT_S | INT_H |   0   || I_DNE | I_LER | I_VER | I_PER ||
+
+// VON - Length Per DMA VMEBus Transfer
+//  0000 = None
+//  0001 = 256 Bytes
+//  0010 = 512
+//  0011 = 1024
+//  0100 = 2048
+//  0101 = 4096
+//  0110 = 8192
+//  0111 = 16384
+
+// VOFF - wait between DMA tenures
+//  0000 = 0    us
+//  0001 = 16
+//  0010 = 32
+//  0011 = 64
+//  0100 = 128
+//  0101 = 256
+//  0110 = 512
+//  0111 = 1024
+
+#endif				/* _ca91c042_h */
--- a/drivers/staging/vme/Kconfig
+++ b/drivers/staging/vme/Kconfig
@@ -10,7 +10,7 @@ menuconfig VME
 
 if VME
 
-#source "drivers/staging/vme/bridges/Kconfig"
+source "drivers/staging/vme/bridges/Kconfig"
 
 source "drivers/staging/vme/devices/Kconfig"
 
--- a/drivers/staging/vme/Makefile
+++ b/drivers/staging/vme/Makefile
@@ -3,5 +3,5 @@
 #
 obj-$(CONFIG_VME)		+= vme.o
 
-#obj-y				+= bridges/
+obj-y				+= bridges/
 obj-y				+= devices/


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

* [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
                     ` (2 preceding siblings ...)
  2009-08-03 21:01   ` [patch 3/5] Staging: vme: add Universe I/II bridge driver Greg K-H
@ 2009-08-03 21:01   ` Greg K-H
  2009-08-03 22:50     ` Jiri Slaby
  2009-08-09  0:09     ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Emilio G. Cota
  2009-08-03 21:01   ` [patch 5/5] Staging: vme: add TODO file Greg K-H
                     ` (2 subsequent siblings)
  6 siblings, 2 replies; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:01 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

[-- Attachment #1: staging-vme-add-tundra-tsi148-vme-pci-bridge-driver.patch --]
[-- Type: text/plain, Size: 137871 bytes --]

From: Martyn Welch <martyn.welch@gefanuc.com>

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/staging/vme/bridges/Kconfig      |    6 
 drivers/staging/vme/bridges/Makefile     |    1 
 drivers/staging/vme/bridges/vme_tsi148.c | 2912 +++++++++++++++++++++++++++++++
 drivers/staging/vme/bridges/vme_tsi148.h | 1387 ++++++++++++++
 4 files changed, 4306 insertions(+)

--- a/drivers/staging/vme/bridges/Kconfig
+++ b/drivers/staging/vme/bridges/Kconfig
@@ -6,3 +6,9 @@ config VME_CA91CX42
 	help
 	 If you say Y here you get support for the Tundra CA91C042 (Universe I)
 	 and CA91C142 (Universe II) VME bridge chips.
+
+config VME_TSI148
+	tristate "Tempe"
+	help
+	 If you say Y here you get support for the Tundra TSI148 VME bridge
+	 chip.
--- a/drivers/staging/vme/bridges/Makefile
+++ b/drivers/staging/vme/bridges/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VME_CA91CX42)	+= vme_ca91cx42.o
+obj-$(CONFIG_VME_TSI148)	+= vme_tsi148.o
--- /dev/null
+++ b/drivers/staging/vme/bridges/vme_tsi148.c
@@ -0,0 +1,2912 @@
+/*
+ * Support for the Tundra TSI148 VME-PCI Bridge Chip
+ *
+ * Author: Martyn Welch <martyn.welch@gefanuc.com>
+ * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by Tom Armistead and Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "../vme.h"
+#include "../vme_bridge.h"
+#include "vme_tsi148.h"
+
+static int __init tsi148_init(void);
+static int tsi148_probe(struct pci_dev *, const struct pci_device_id *);
+static void tsi148_remove(struct pci_dev *);
+static void __exit tsi148_exit(void);
+
+
+int tsi148_slave_set(struct vme_slave_resource *, int, unsigned long long,
+	unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
+int tsi148_slave_get(struct vme_slave_resource *, int *, unsigned long long *,
+	unsigned long long *, dma_addr_t *, vme_address_t *, vme_cycle_t *);
+
+int tsi148_master_get(struct vme_master_resource *, int *, unsigned long long *,
+        unsigned long long *, vme_address_t *, vme_cycle_t *, vme_width_t *);
+int tsi148_master_set(struct vme_master_resource *, int, unsigned long long,
+	unsigned long long, vme_address_t, vme_cycle_t,	vme_width_t);
+ssize_t tsi148_master_read(struct vme_master_resource *, void *, size_t,
+	loff_t);
+ssize_t tsi148_master_write(struct vme_master_resource *, void *, size_t,
+	loff_t);
+unsigned int tsi148_master_rmw(struct vme_master_resource *, unsigned int,
+	unsigned int, unsigned int, loff_t);
+int tsi148_dma_list_add (struct vme_dma_list *, struct vme_dma_attr *,
+	struct vme_dma_attr *, size_t);
+int tsi148_dma_list_exec(struct vme_dma_list *);
+int tsi148_dma_list_empty(struct vme_dma_list *);
+int tsi148_generate_irq(int, int);
+int tsi148_lm_set(unsigned long long, vme_address_t, vme_cycle_t);
+int tsi148_lm_get(unsigned long long *, vme_address_t *, vme_cycle_t *);
+int tsi148_lm_attach(int, void (*callback)(int));
+int tsi148_lm_detach(int);
+int tsi148_slot_get(void);
+
+/* Modue parameter */
+int err_chk = 0;
+
+/* XXX These should all be in a per device structure */
+struct vme_bridge *tsi148_bridge;
+wait_queue_head_t dma_queue[2];
+wait_queue_head_t iack_queue;
+void (*lm_callback[4])(int);	/* Called in interrupt handler, be careful! */
+void *crcsr_kernel;
+dma_addr_t crcsr_bus;
+struct vme_master_resource *flush_image;
+struct semaphore vme_rmw;	/* Only one RMW cycle at a time */
+struct semaphore vme_int;	/*
+				 * Only one VME interrupt can be
+				 * generated at a time, provide locking
+				 */
+struct semaphore vme_irq;	/* Locking for VME irq callback configuration */
+struct semaphore vme_lm;	/* Locking for location monitor operations */
+
+
+static char driver_name[] = "vme_tsi148";
+
+static struct pci_device_id tsi148_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) },
+	{ },
+};
+
+static struct pci_driver tsi148_driver = {
+	.name = driver_name,
+	.id_table = tsi148_ids,
+	.probe = tsi148_probe,
+	.remove = tsi148_remove,
+};
+
+static void reg_join(unsigned int high, unsigned int low,
+	unsigned long long *variable)
+{
+	*variable = (unsigned long long)high << 32;
+	*variable |= (unsigned long long)low;
+}
+
+static void reg_split(unsigned long long variable, unsigned int *high,
+	unsigned int *low)
+{
+	*low = (unsigned int)variable & 0xFFFFFFFF;
+	*high = (unsigned int)(variable >> 32);
+}
+
+/*
+ * Wakes up DMA queue.
+ */
+static u32 tsi148_DMA_irqhandler(int channel_mask)
+{
+	u32 serviced = 0;
+
+	if (channel_mask & TSI148_LCSR_INTS_DMA0S) {
+		wake_up(&dma_queue[0]);
+		serviced |= TSI148_LCSR_INTC_DMA0C;
+	}
+	if (channel_mask & TSI148_LCSR_INTS_DMA1S) {
+		wake_up(&dma_queue[1]);
+		serviced |= TSI148_LCSR_INTC_DMA1C;
+	}
+
+	return serviced;
+}
+
+/*
+ * Wake up location monitor queue
+ */
+static u32 tsi148_LM_irqhandler(u32 stat)
+{
+	int i;
+	u32 serviced = 0;
+
+	for (i = 0; i < 4; i++) {
+		if(stat & TSI148_LCSR_INTS_LMS[i]) {
+			/* We only enable interrupts if the callback is set */
+			lm_callback[i](i);
+			serviced |= TSI148_LCSR_INTC_LMC[i];
+		}
+	}
+
+	return serviced;
+}
+
+/*
+ * Wake up mail box queue.
+ *
+ * XXX This functionality is not exposed up though API.
+ */
+static u32 tsi148_MB_irqhandler(u32 stat)
+{
+	int i;
+	u32 val;
+	u32 serviced = 0;
+
+	for (i = 0; i < 4; i++) {
+		if(stat & TSI148_LCSR_INTS_MBS[i]) {
+			val = ioread32be(tsi148_bridge->base +
+				TSI148_GCSR_MBOX[i]);
+			printk("VME Mailbox %d received: 0x%x\n", i, val);
+			serviced |= TSI148_LCSR_INTC_MBC[i];
+		}
+	}
+
+	return serviced;
+}
+
+/*
+ * Display error & status message when PERR (PCI) exception interrupt occurs.
+ */
+static u32 tsi148_PERR_irqhandler(void)
+{
+	printk(KERN_ERR
+		"PCI Exception at address: 0x%08x:%08x, attributes: %08x\n",
+		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAU),
+		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAL),
+		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAT)
+		);
+	printk(KERN_ERR
+		"PCI-X attribute reg: %08x, PCI-X split completion reg: %08x\n",
+		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPXA),
+		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPXS)
+		);
+
+	iowrite32be(TSI148_LCSR_EDPAT_EDPCL,
+		tsi148_bridge->base + TSI148_LCSR_EDPAT);
+
+	return TSI148_LCSR_INTC_PERRC;
+}
+
+/*
+ * Save address and status when VME error interrupt occurs.
+ */
+static u32 tsi148_VERR_irqhandler(void)
+{
+	unsigned int error_addr_high, error_addr_low;
+	unsigned long long error_addr;
+	u32 error_attrib;
+	struct vme_bus_error *error;
+
+	error_addr_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAU);
+	error_addr_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAL);
+	error_attrib = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAT);
+
+	reg_join(error_addr_high, error_addr_low, &error_addr);
+
+	/* Check for exception register overflow (we have lost error data) */
+	if(error_attrib & TSI148_LCSR_VEAT_VEOF) {
+		printk(KERN_ERR "VME Bus Exception Overflow Occurred\n");
+	}
+
+	error = (struct vme_bus_error *)kmalloc(sizeof (struct vme_bus_error),
+		GFP_ATOMIC);
+	if (error) {
+		error->address = error_addr;
+		error->attributes = error_attrib;
+		list_add_tail(&(error->list), &(tsi148_bridge->vme_errors));
+	} else {
+		printk(KERN_ERR
+			"Unable to alloc memory for VMEbus Error reporting\n");
+		printk(KERN_ERR
+			"VME Bus Error at address: 0x%llx, attributes: %08x\n",
+			error_addr, error_attrib);
+	}
+
+	/* Clear Status */
+	iowrite32be(TSI148_LCSR_VEAT_VESCL,
+		tsi148_bridge->base + TSI148_LCSR_VEAT);
+
+	return TSI148_LCSR_INTC_VERRC;
+}
+
+/*
+ * Wake up IACK queue.
+ */
+static u32 tsi148_IACK_irqhandler(void)
+{
+	printk("tsi148_IACK_irqhandler\n");
+	wake_up(&iack_queue);
+
+	return TSI148_LCSR_INTC_IACKC;
+}
+
+/*
+ * Calling VME bus interrupt callback if provided.
+ */
+static u32 tsi148_VIRQ_irqhandler(u32 stat)
+{
+	int vec, i, serviced = 0;
+	void (*call)(int, int, void *);
+	void *priv_data;
+
+	for (i = 7; i > 0; i--) {
+		if (stat & (1 << i)) {
+			/*
+			 * 	Note:   Even though the registers are defined
+			 * 	as 32-bits in the spec, we only want to issue
+			 * 	8-bit IACK cycles on the bus, read from offset
+			 * 	3.
+			 */
+			vec = ioread8(tsi148_bridge->base +
+				TSI148_LCSR_VIACK[i] + 3);
+
+			call = tsi148_bridge->irq[i - 1].callback[vec].func;
+			priv_data =
+				tsi148_bridge->irq[i-1].callback[vec].priv_data;
+
+			if (call != NULL)
+				call(i, vec, priv_data);
+			else
+				printk("Spurilous VME interrupt, level:%x, "
+					"vector:%x\n", i, vec);
+
+			serviced |= (1 << i);
+		}
+	}
+
+	return serviced;
+}
+
+/*
+ * Top level interrupt handler.  Clears appropriate interrupt status bits and
+ * then calls appropriate sub handler(s).
+ */
+static irqreturn_t tsi148_irqhandler(int irq, void *dev_id)
+{
+	u32 stat, enable, serviced = 0;
+
+	/* Determine which interrupts are unmasked and set */
+	enable = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+	stat = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTS);
+
+	/* Only look at unmasked interrupts */
+	stat &= enable;
+
+	if (unlikely(!stat)) {
+		return IRQ_NONE;
+	}
+
+	/* Call subhandlers as appropriate */
+	/* DMA irqs */
+	if (stat & (TSI148_LCSR_INTS_DMA1S | TSI148_LCSR_INTS_DMA0S))
+		serviced |= tsi148_DMA_irqhandler(stat);
+
+	/* Location monitor irqs */
+	if (stat & (TSI148_LCSR_INTS_LM3S | TSI148_LCSR_INTS_LM2S |
+			TSI148_LCSR_INTS_LM1S | TSI148_LCSR_INTS_LM0S))
+		serviced |= tsi148_LM_irqhandler(stat);
+
+	/* Mail box irqs */
+	if (stat & (TSI148_LCSR_INTS_MB3S | TSI148_LCSR_INTS_MB2S |
+			TSI148_LCSR_INTS_MB1S | TSI148_LCSR_INTS_MB0S))
+		serviced |= tsi148_MB_irqhandler(stat);
+
+	/* PCI bus error */
+	if (stat & TSI148_LCSR_INTS_PERRS)
+		serviced |= tsi148_PERR_irqhandler();
+
+	/* VME bus error */
+	if (stat & TSI148_LCSR_INTS_VERRS)
+		serviced |= tsi148_VERR_irqhandler();
+
+	/* IACK irq */
+	if (stat & TSI148_LCSR_INTS_IACKS)
+		serviced |= tsi148_IACK_irqhandler();
+
+	/* VME bus irqs */
+	if (stat & (TSI148_LCSR_INTS_IRQ7S | TSI148_LCSR_INTS_IRQ6S |
+			TSI148_LCSR_INTS_IRQ5S | TSI148_LCSR_INTS_IRQ4S |
+			TSI148_LCSR_INTS_IRQ3S | TSI148_LCSR_INTS_IRQ2S |
+			TSI148_LCSR_INTS_IRQ1S))
+		serviced |= tsi148_VIRQ_irqhandler(stat);
+
+	/* Clear serviced interrupts */
+	iowrite32be(serviced, tsi148_bridge->base + TSI148_LCSR_INTC);
+
+	return IRQ_HANDLED;
+}
+
+static int tsi148_irq_init(struct vme_bridge *bridge)
+{
+	int result;
+	unsigned int tmp;
+	struct pci_dev *pdev;
+
+	/* Need pdev */
+        pdev = container_of(bridge->parent, struct pci_dev, dev);
+
+	/* Initialise list for VME bus errors */
+	INIT_LIST_HEAD(&(bridge->vme_errors));
+
+	result = request_irq(pdev->irq,
+			     tsi148_irqhandler,
+			     IRQF_SHARED,
+			     driver_name, pdev);
+	if (result) {
+		dev_err(&pdev->dev, "Can't get assigned pci irq vector %02X\n",
+			pdev->irq);
+		return result;
+	}
+
+	/* Enable and unmask interrupts */
+	tmp = TSI148_LCSR_INTEO_DMA1EO | TSI148_LCSR_INTEO_DMA0EO |
+		TSI148_LCSR_INTEO_MB3EO | TSI148_LCSR_INTEO_MB2EO |
+		TSI148_LCSR_INTEO_MB1EO | TSI148_LCSR_INTEO_MB0EO |
+		TSI148_LCSR_INTEO_PERREO | TSI148_LCSR_INTEO_VERREO |
+		TSI148_LCSR_INTEO_IACKEO;
+
+	/* XXX This leaves the following interrupts masked.
+	 * TSI148_LCSR_INTEO_VIEEO
+	 * TSI148_LCSR_INTEO_SYSFLEO
+	 * TSI148_LCSR_INTEO_ACFLEO
+	 */
+
+	/* Don't enable Location Monitor interrupts here - they will be
+	 * enabled when the location monitors are properly configured and
+	 * a callback has been attached.
+	 * TSI148_LCSR_INTEO_LM0EO
+	 * TSI148_LCSR_INTEO_LM1EO
+	 * TSI148_LCSR_INTEO_LM2EO
+	 * TSI148_LCSR_INTEO_LM3EO
+	 */
+
+	/* Don't enable VME interrupts until we add a handler, else the board
+	 * will respond to it and we don't want that unless it knows how to
+	 * properly deal with it.
+	 * TSI148_LCSR_INTEO_IRQ7EO
+	 * TSI148_LCSR_INTEO_IRQ6EO
+	 * TSI148_LCSR_INTEO_IRQ5EO
+	 * TSI148_LCSR_INTEO_IRQ4EO
+	 * TSI148_LCSR_INTEO_IRQ3EO
+	 * TSI148_LCSR_INTEO_IRQ2EO
+	 * TSI148_LCSR_INTEO_IRQ1EO
+	 */
+
+	iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO);
+	iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN);
+
+	return 0;
+}
+
+static void tsi148_irq_exit(struct pci_dev *pdev)
+{
+	/* Turn off interrupts */
+	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEO);
+	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+	/* Clear all interrupts */
+	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTC);
+
+	/* Detach interrupt handler */
+	free_irq(pdev->irq, pdev);
+}
+
+/*
+ * Check to see if an IACk has been received, return true (1) or false (0).
+ */
+int tsi148_iack_received(void)
+{
+	u32 tmp;
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR);
+
+	if (tmp & TSI148_LCSR_VICR_IRQS)
+		return 0;
+	else
+		return 1;
+}
+
+/*
+ * Set up an VME interrupt
+ */
+int tsi148_request_irq(int level, int statid,
+	void (*callback)(int level, int vector, void *priv_data),
+        void *priv_data)
+{
+	u32 tmp;
+
+	/* Get semaphore */
+	down(&(vme_irq));
+
+	if(tsi148_bridge->irq[level - 1].callback[statid].func) {
+		up(&(vme_irq));
+		printk("VME Interrupt already taken\n");
+		return -EBUSY;
+	}
+
+
+	tsi148_bridge->irq[level - 1].count++;
+	tsi148_bridge->irq[level - 1].callback[statid].priv_data = priv_data;
+	tsi148_bridge->irq[level - 1].callback[statid].func = callback;
+
+	/* Enable IRQ level */
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+	tmp |= TSI148_LCSR_INTEO_IRQEO[level - 1];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
+	tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+	/* Release semaphore */
+	up(&(vme_irq));
+
+	return 0;
+}
+
+/*
+ * Free VME interrupt
+ */
+void tsi148_free_irq(int level, int statid)
+{
+	u32 tmp;
+
+	/* Get semaphore */
+	down(&(vme_irq));
+
+	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
+	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
+	tsi148_bridge->irq[level - 1].count--;
+
+	/* Disable IRQ level */
+	if (tsi148_bridge->irq[level - 1].count == 0) {
+		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
+		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
+
+		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
+		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
+		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
+	}
+
+	/* Release semaphore */
+	up(&(vme_irq));
+}
+
+/*
+ * Generate a VME bus interrupt at the requested level & vector. Wait for
+ * interrupt to be acked.
+ *
+ * Only one interrupt can be generated at a time - so add a semaphore.
+ */
+int tsi148_generate_irq(int level, int statid)
+{
+	u32 tmp;
+
+	/* Get semaphore */
+	down(&(vme_int));
+
+	/* Read VICR register */
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR);
+
+	/* Set Status/ID */
+	tmp = (tmp & ~TSI148_LCSR_VICR_STID_M) |
+		(statid & TSI148_LCSR_VICR_STID_M);
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VICR);
+
+	/* Assert VMEbus IRQ */
+	tmp = tmp | TSI148_LCSR_VICR_IRQL[level];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VICR);
+
+	/* XXX Consider implementing a timeout? */
+	wait_event_interruptible(iack_queue, tsi148_iack_received());
+
+	/* Release semaphore */
+	up(&(vme_int));
+
+	return 0;
+}
+
+/*
+ * Find the first error in this address range
+ */
+static struct vme_bus_error *tsi148_find_error(vme_address_t aspace,
+	unsigned long long address, size_t count)
+{
+	struct list_head *err_pos;
+	struct vme_bus_error *vme_err, *valid = NULL;
+	unsigned long long bound;
+
+	bound = address + count;
+
+	/*
+	 * XXX We are currently not looking at the address space when parsing
+	 *     for errors. This is because parsing the Address Modifier Codes
+	 *     is going to be quite resource intensive to do properly. We
+	 *     should be OK just looking at the addresses and this is certainly
+	 *     much better than what we had before.
+	 */
+	err_pos = NULL;
+	/* Iterate through errors */
+	list_for_each(err_pos, &(tsi148_bridge->vme_errors)) {
+		vme_err = list_entry(err_pos, struct vme_bus_error, list);
+		if((vme_err->address >= address) && (vme_err->address < bound)){
+			valid = vme_err;
+			break;
+		}
+	}
+
+	return valid;
+}
+
+/*
+ * Clear errors in the provided address range.
+ */
+static void tsi148_clear_errors(vme_address_t aspace,
+	unsigned long long address, size_t count)
+{
+	struct list_head *err_pos, *temp;
+	struct vme_bus_error *vme_err;
+	unsigned long long bound;
+
+	bound = address + count;
+
+	/*
+	 * XXX We are currently not looking at the address space when parsing
+	 *     for errors. This is because parsing the Address Modifier Codes
+	 *     is going to be quite resource intensive to do properly. We
+	 *     should be OK just looking at the addresses and this is certainly
+	 *     much better than what we had before.
+	 */
+	err_pos = NULL;
+	/* Iterate through errors */
+	list_for_each_safe(err_pos, temp, &(tsi148_bridge->vme_errors)) {
+		vme_err = list_entry(err_pos, struct vme_bus_error, list);
+
+		if((vme_err->address >= address) && (vme_err->address < bound)){
+			list_del(err_pos);
+			kfree(vme_err);
+		}
+	}
+}
+
+/*
+ * Initialize a slave window with the requested attributes.
+ */
+int tsi148_slave_set(struct vme_slave_resource *image, int enabled,
+	unsigned long long vme_base, unsigned long long size,
+	dma_addr_t pci_base, vme_address_t aspace, vme_cycle_t cycle)
+{
+	unsigned int i, addr = 0, granularity = 0;
+	unsigned int temp_ctl = 0;
+	unsigned int vme_base_low, vme_base_high;
+	unsigned int vme_bound_low, vme_bound_high;
+	unsigned int pci_offset_low, pci_offset_high;
+	unsigned long long vme_bound, pci_offset;
+
+#if 0
+        printk("Set slave image %d to:\n", image->number);
+ 	printk("\tEnabled: %s\n", (enabled == 1)? "yes" : "no");
+	printk("\tVME Base:0x%llx\n", vme_base);
+	printk("\tWindow Size:0x%llx\n", size);
+	printk("\tPCI Base:0x%lx\n", (unsigned long)pci_base);
+	printk("\tAddress Space:0x%x\n", aspace);
+	printk("\tTransfer Cycle Properties:0x%x\n", cycle);
+#endif
+
+	i = image->number;
+
+	switch (aspace) {
+	case VME_A16:
+		granularity = 0x10;
+		addr |= TSI148_LCSR_ITAT_AS_A16;
+		break;
+	case VME_A24:
+		granularity = 0x1000;
+		addr |= TSI148_LCSR_ITAT_AS_A24;
+		break;
+	case VME_A32:
+		granularity = 0x10000;
+		addr |= TSI148_LCSR_ITAT_AS_A32;
+		break;
+	case VME_A64:
+		granularity = 0x10000;
+		addr |= TSI148_LCSR_ITAT_AS_A64;
+		break;
+	case VME_CRCSR:
+	case VME_USER1:
+	case VME_USER2:
+	case VME_USER3:
+	case VME_USER4:
+	default:
+		printk("Invalid address space\n");
+		return -EINVAL;
+		break;
+	}
+
+	/* Convert 64-bit variables to 2x 32-bit variables */
+	reg_split(vme_base, &vme_base_high, &vme_base_low);
+
+	/*
+	 * Bound address is a valid address for the window, adjust
+	 * accordingly
+	 */
+	vme_bound = vme_base + size - granularity;
+	reg_split(vme_bound, &vme_bound_high, &vme_bound_low);
+	pci_offset = (unsigned long long)pci_base - vme_base;
+	reg_split(pci_offset, &pci_offset_high, &pci_offset_low);
+
+	if (vme_base_low & (granularity - 1)) {
+		printk("Invalid VME base alignment\n");
+		return -EINVAL;
+	}
+	if (vme_bound_low & (granularity - 1)) {
+		printk("Invalid VME bound alignment\n");
+		return -EINVAL;
+	}
+	if (pci_offset_low & (granularity - 1)) {
+		printk("Invalid PCI Offset alignment\n");
+		return -EINVAL;
+	}
+
+#if 0
+	printk("\tVME Bound:0x%llx\n", vme_bound);
+	printk("\tPCI Offset:0x%llx\n", pci_offset);
+#endif
+
+	/*  Disable while we are mucking around */
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITAT);
+	temp_ctl &= ~TSI148_LCSR_ITAT_EN;
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITAT);
+
+	/* Setup mapping */
+	iowrite32be(vme_base_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITSAU);
+	iowrite32be(vme_base_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITSAL);
+	iowrite32be(vme_bound_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITEAU);
+	iowrite32be(vme_bound_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITEAL);
+	iowrite32be(pci_offset_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITOFU);
+	iowrite32be(pci_offset_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITOFL);
+
+/* XXX Prefetch stuff currently unsupported */
+#if 0
+
+	for (x = 0; x < 4; x++) {
+		if ((64 << x) >= vmeIn->prefetchSize) {
+			break;
+		}
+	}
+	if (x == 4)
+		x--;
+	temp_ctl |= (x << 16);
+
+	if (vmeIn->prefetchThreshold)
+		if (vmeIn->prefetchThreshold)
+			temp_ctl |= 0x40000;
+#endif
+
+	/* Setup 2eSST speeds */
+	temp_ctl &= ~TSI148_LCSR_ITAT_2eSSTM_M;
+	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
+	case VME_2eSST160:
+		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_160;
+		break;
+	case VME_2eSST267:
+		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_267;
+		break;
+	case VME_2eSST320:
+		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_320;
+		break;
+	}
+
+	/* Setup cycle types */
+	temp_ctl &= ~(0x1F << 7);
+	if (cycle & VME_BLT)
+		temp_ctl |= TSI148_LCSR_ITAT_BLT;
+	if (cycle & VME_MBLT)
+		temp_ctl |= TSI148_LCSR_ITAT_MBLT;
+	if (cycle & VME_2eVME)
+		temp_ctl |= TSI148_LCSR_ITAT_2eVME;
+	if (cycle & VME_2eSST)
+		temp_ctl |= TSI148_LCSR_ITAT_2eSST;
+	if (cycle & VME_2eSSTB)
+		temp_ctl |= TSI148_LCSR_ITAT_2eSSTB;
+
+	/* Setup address space */
+	temp_ctl &= ~TSI148_LCSR_ITAT_AS_M;
+	temp_ctl |= addr;
+
+	temp_ctl &= ~0xF;
+	if (cycle & VME_SUPER)
+		temp_ctl |= TSI148_LCSR_ITAT_SUPR ;
+	if (cycle & VME_USER)
+		temp_ctl |= TSI148_LCSR_ITAT_NPRIV;
+	if (cycle & VME_PROG)
+		temp_ctl |= TSI148_LCSR_ITAT_PGM;
+	if (cycle & VME_DATA)
+		temp_ctl |= TSI148_LCSR_ITAT_DATA;
+
+	/* Write ctl reg without enable */
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITAT);
+
+	if (enabled)
+		temp_ctl |= TSI148_LCSR_ITAT_EN;
+
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITAT);
+
+	return 0;
+}
+
+/*
+ * Get slave window configuration.
+ *
+ * XXX Prefetch currently unsupported.
+ */
+int tsi148_slave_get(struct vme_slave_resource *image, int *enabled,
+	unsigned long long *vme_base, unsigned long long *size,
+	dma_addr_t *pci_base, vme_address_t *aspace, vme_cycle_t *cycle)
+{
+	unsigned int i, granularity = 0, ctl = 0;
+	unsigned int vme_base_low, vme_base_high;
+	unsigned int vme_bound_low, vme_bound_high;
+	unsigned int pci_offset_low, pci_offset_high;
+	unsigned long long vme_bound, pci_offset;
+
+
+	i = image->number;
+
+	/* Read registers */
+	ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITAT);
+
+	vme_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITSAU);
+	vme_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITSAL);
+	vme_bound_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITEAU);
+	vme_bound_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITEAL);
+	pci_offset_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITOFU);
+	pci_offset_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
+		TSI148_LCSR_OFFSET_ITOFL);
+
+	/* Convert 64-bit variables to 2x 32-bit variables */
+	reg_join(vme_base_high, vme_base_low, vme_base);
+	reg_join(vme_bound_high, vme_bound_low, &vme_bound);
+	reg_join(pci_offset_high, pci_offset_low, &pci_offset);
+
+	*pci_base = (dma_addr_t)vme_base + pci_offset;
+
+	*enabled = 0;
+	*aspace = 0;
+	*cycle = 0;
+
+	if (ctl & TSI148_LCSR_ITAT_EN)
+		*enabled = 1;
+
+	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A16) {
+		granularity = 0x10;
+		*aspace |= VME_A16;
+	}
+	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A24) {
+		granularity = 0x1000;
+		*aspace |= VME_A24;
+	}
+	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A32) {
+		granularity = 0x10000;
+		*aspace |= VME_A32;
+	}
+	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A64) {
+		granularity = 0x10000;
+		*aspace |= VME_A64;
+	}
+
+	/* Need granularity before we set the size */
+	*size = (unsigned long long)((vme_bound - *vme_base) + granularity);
+
+
+	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_160)
+		*cycle |= VME_2eSST160;
+	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_267)
+		*cycle |= VME_2eSST267;
+	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_320)
+		*cycle |= VME_2eSST320;
+
+	if (ctl & TSI148_LCSR_ITAT_BLT)
+		*cycle |= VME_BLT;
+	if (ctl & TSI148_LCSR_ITAT_MBLT)
+		*cycle |= VME_MBLT;
+	if (ctl & TSI148_LCSR_ITAT_2eVME)
+		*cycle |= VME_2eVME;
+	if (ctl & TSI148_LCSR_ITAT_2eSST)
+		*cycle |= VME_2eSST;
+	if (ctl & TSI148_LCSR_ITAT_2eSSTB)
+		*cycle |= VME_2eSSTB;
+
+	if (ctl & TSI148_LCSR_ITAT_SUPR)
+		*cycle |= VME_SUPER;
+	if (ctl & TSI148_LCSR_ITAT_NPRIV)
+		*cycle |= VME_USER;
+	if (ctl & TSI148_LCSR_ITAT_PGM)
+		*cycle |= VME_PROG;
+	if (ctl & TSI148_LCSR_ITAT_DATA)
+		*cycle |= VME_DATA;
+
+	return 0;
+}
+
+/*
+ * Allocate and map PCI Resource
+ */
+static int tsi148_alloc_resource(struct vme_master_resource *image,
+	unsigned long long size)
+{
+	unsigned long long existing_size;
+	int retval = 0;
+	struct pci_dev *pdev;
+
+	/* Find pci_dev container of dev */
+        if (tsi148_bridge->parent == NULL) {
+                printk("Dev entry NULL\n");
+                return -EINVAL;
+        }
+        pdev = container_of(tsi148_bridge->parent, struct pci_dev, dev);
+
+	existing_size = (unsigned long long)(image->pci_resource.end -
+		image->pci_resource.start);
+
+	/* If the existing size is OK, return */
+	if (existing_size == (size - 1))
+		return 0;
+
+	if (existing_size != 0) {
+		iounmap(image->kern_base);
+		image->kern_base = NULL;
+		if (image->pci_resource.name != NULL)
+			kfree(image->pci_resource.name);
+		release_resource(&(image->pci_resource));
+		memset(&(image->pci_resource), 0, sizeof(struct resource));
+	}
+
+	if (image->pci_resource.name == NULL) {
+		image->pci_resource.name = kmalloc(VMENAMSIZ+3, GFP_KERNEL);
+		if (image->pci_resource.name == NULL) {
+			printk(KERN_ERR "Unable to allocate memory for resource"
+				" name\n");
+			retval = -ENOMEM;
+			goto err_name;
+		}
+	}
+
+	sprintf((char *)image->pci_resource.name, "%s.%d", tsi148_bridge->name,
+		image->number);
+
+	image->pci_resource.start = 0;
+	image->pci_resource.end = (unsigned long)size;
+	image->pci_resource.flags = IORESOURCE_MEM;
+
+	retval = pci_bus_alloc_resource(pdev->bus,
+		&(image->pci_resource), size, size, PCIBIOS_MIN_MEM,
+		0, NULL, NULL);
+	if (retval) {
+		printk(KERN_ERR "Failed to allocate mem resource for "
+			"window %d size 0x%lx start 0x%lx\n",
+			image->number, (unsigned long)size,
+			(unsigned long)image->pci_resource.start);
+		goto err_resource;
+	}
+
+	image->kern_base = ioremap_nocache(
+		image->pci_resource.start, size);
+	if (image->kern_base == NULL) {
+		printk(KERN_ERR "Failed to remap resource\n");
+		retval = -ENOMEM;
+		goto err_remap;
+	}
+
+	return 0;
+
+	iounmap(image->kern_base);
+	image->kern_base = NULL;
+err_remap:
+	release_resource(&(image->pci_resource));
+err_resource:
+	kfree(image->pci_resource.name);
+	memset(&(image->pci_resource), 0, sizeof(struct resource));
+err_name:
+	return retval;
+}
+
+/*
+ * Free and unmap PCI Resource
+ */
+static void tsi148_free_resource(struct vme_master_resource *image)
+{
+	iounmap(image->kern_base);
+	image->kern_base = NULL;
+	release_resource(&(image->pci_resource));
+	kfree(image->pci_resource.name);
+	memset(&(image->pci_resource), 0, sizeof(struct resource));
+}
+
+/*
+ * Set the attributes of an outbound window.
+ */
+int tsi148_master_set( struct vme_master_resource *image, int enabled,
+	unsigned long long vme_base, unsigned long long size,
+	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
+{
+	int retval = 0;
+	unsigned int i;
+	unsigned int temp_ctl = 0;
+	unsigned int pci_base_low, pci_base_high;
+	unsigned int pci_bound_low, pci_bound_high;
+	unsigned int vme_offset_low, vme_offset_high;
+	unsigned long long pci_bound, vme_offset, pci_base;
+
+	/* Verify input data */
+	if (vme_base & 0xFFFF) {
+		printk("Invalid VME Window alignment\n");
+		retval = -EINVAL;
+		goto err_window;
+	}
+	if (size < 0x10000) {
+		printk("Invalid VME Window size\n");
+		retval = -EINVAL;
+		goto err_window;
+	}
+
+	spin_lock(&(image->lock));
+
+	/* Let's allocate the resource here rather than further up the stack as
+	 * it avoids pushing loads of bus dependant stuff up the stack
+	 */
+	retval = tsi148_alloc_resource(image, size);
+	if (retval) {
+		spin_unlock(&(image->lock));
+		printk(KERN_ERR "Unable to allocate memory for resource "
+			"name\n");
+		retval = -ENOMEM;
+		goto err_res;
+	}
+
+	pci_base = (unsigned long long)image->pci_resource.start;
+
+
+	/*
+	 * Bound address is a valid address for the window, adjust
+	 * according to window granularity.
+	 */
+	pci_bound = pci_base + (size - 0x10000);
+	vme_offset = vme_base - pci_base;
+
+	/* Convert 64-bit variables to 2x 32-bit variables */
+	reg_split(pci_base, &pci_base_high, &pci_base_low);
+	reg_split(pci_bound, &pci_bound_high, &pci_bound_low);
+	reg_split(vme_offset, &vme_offset_high, &vme_offset_low);
+
+	if (pci_base_low & 0xFFFF) {
+		spin_unlock(&(image->lock));
+		printk("Invalid PCI base alignment\n");
+		retval = -EINVAL;
+		goto err_gran;
+	}
+	if (pci_bound_low & 0xFFFF) {
+		spin_unlock(&(image->lock));
+		printk("Invalid PCI bound alignment\n");
+		retval = -EINVAL;
+		goto err_gran;
+	}
+	if (vme_offset_low & 0xFFFF) {
+		spin_unlock(&(image->lock));
+		printk("Invalid VME Offset alignment\n");
+		retval = -EINVAL;
+		goto err_gran;
+	}
+
+	i = image->number;
+
+	/* Disable while we are mucking around */
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTAT);
+	temp_ctl &= ~TSI148_LCSR_OTAT_EN;
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTAT);
+
+/* XXX Prefetch stuff currently unsupported */
+#if 0
+	if (vmeOut->prefetchEnable) {
+		temp_ctl |= 0x40000;
+		for (x = 0; x < 4; x++) {
+			if ((2 << x) >= vmeOut->prefetchSize)
+				break;
+		}
+		if (x == 4)
+			x = 3;
+		temp_ctl |= (x << 16);
+	}
+#endif
+
+	/* Setup 2eSST speeds */
+	temp_ctl &= ~TSI148_LCSR_OTAT_2eSSTM_M;
+	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
+	case VME_2eSST160:
+		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_160;
+		break;
+	case VME_2eSST267:
+		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_267;
+		break;
+	case VME_2eSST320:
+		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_320;
+		break;
+	}
+
+	/* Setup cycle types */
+	if (cycle & VME_BLT) {
+		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
+		temp_ctl |= TSI148_LCSR_OTAT_TM_BLT;
+	}
+	if (cycle & VME_MBLT) {
+		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
+		temp_ctl |= TSI148_LCSR_OTAT_TM_MBLT;
+	}
+	if (cycle & VME_2eVME) {
+		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
+		temp_ctl |= TSI148_LCSR_OTAT_TM_2eVME;
+	}
+	if (cycle & VME_2eSST) {
+		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
+		temp_ctl |= TSI148_LCSR_OTAT_TM_2eSST;
+	}
+	if (cycle & VME_2eSSTB) {
+		printk("Currently not setting Broadcast Select Registers\n");
+		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
+		temp_ctl |= TSI148_LCSR_OTAT_TM_2eSSTB;
+	}
+
+	/* Setup data width */
+	temp_ctl &= ~TSI148_LCSR_OTAT_DBW_M;
+	switch (dwidth) {
+	case VME_D16:
+		temp_ctl |= TSI148_LCSR_OTAT_DBW_16;
+		break;
+	case VME_D32:
+		temp_ctl |= TSI148_LCSR_OTAT_DBW_32;
+		break;
+	default:
+		spin_unlock(&(image->lock));
+		printk("Invalid data width\n");
+		retval = -EINVAL;
+		goto err_dwidth;
+	}
+
+	/* Setup address space */
+	temp_ctl &= ~TSI148_LCSR_OTAT_AMODE_M;
+	switch (aspace) {
+	case VME_A16:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A16;
+		break;
+	case VME_A24:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A24;
+		break;
+	case VME_A32:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A32;
+		break;
+	case VME_A64:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A64;
+		break;
+	case VME_CRCSR:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_CRCSR;
+		break;
+	case VME_USER1:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER1;
+		break;
+	case VME_USER2:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER2;
+		break;
+	case VME_USER3:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER3;
+		break;
+	case VME_USER4:
+		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER4;
+		break;
+	default:
+		spin_unlock(&(image->lock));
+		printk("Invalid address space\n");
+		retval = -EINVAL;
+		goto err_aspace;
+		break;
+	}
+
+	temp_ctl &= ~(3<<4);
+	if (cycle & VME_SUPER)
+		temp_ctl |= TSI148_LCSR_OTAT_SUP;
+	if (cycle & VME_PROG)
+		temp_ctl |= TSI148_LCSR_OTAT_PGM;
+
+	/* Setup mapping */
+	iowrite32be(pci_base_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAU);
+	iowrite32be(pci_base_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAL);
+	iowrite32be(pci_bound_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTEAU);
+	iowrite32be(pci_bound_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTEAL);
+	iowrite32be(vme_offset_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTOFU);
+	iowrite32be(vme_offset_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTOFL);
+
+/* XXX We need to deal with OTBS */
+#if 0
+	iowrite32be(vmeOut->bcastSelect2esst, tsi148_bridge->base +
+		TSI148_LCSR_OT[i] + TSI148_LCSR_OFFSET_OTBS);
+#endif
+
+	/* Write ctl reg without enable */
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTAT);
+
+	if (enabled)
+		temp_ctl |= TSI148_LCSR_OTAT_EN;
+
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTAT);
+
+	spin_unlock(&(image->lock));
+	return 0;
+
+err_aspace:
+err_dwidth:
+err_gran:
+	tsi148_free_resource(image);
+err_res:
+err_window:
+	return retval;
+
+}
+
+/*
+ * Set the attributes of an outbound window.
+ *
+ * XXX Not parsing prefetch information.
+ */
+int __tsi148_master_get( struct vme_master_resource *image, int *enabled,
+	unsigned long long *vme_base, unsigned long long *size,
+	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
+{
+	unsigned int i, ctl;
+	unsigned int pci_base_low, pci_base_high;
+	unsigned int pci_bound_low, pci_bound_high;
+	unsigned int vme_offset_low, vme_offset_high;
+
+	unsigned long long pci_base, pci_bound, vme_offset;
+
+	i = image->number;
+
+	ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTAT);
+
+	pci_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAU);
+	pci_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAL);
+	pci_bound_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTEAU);
+	pci_bound_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTEAL);
+	vme_offset_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTOFU);
+	vme_offset_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTOFL);
+
+	/* Convert 64-bit variables to 2x 32-bit variables */
+	reg_join(pci_base_high, pci_base_low, &pci_base);
+	reg_join(pci_bound_high, pci_bound_low, &pci_bound);
+	reg_join(vme_offset_high, vme_offset_low, &vme_offset);
+
+	*vme_base = pci_base + vme_offset;
+	*size = (unsigned long long)(pci_bound - pci_base) + 0x10000;
+
+	*enabled = 0;
+	*aspace = 0;
+	*cycle = 0;
+	*dwidth = 0;
+
+	if (ctl & TSI148_LCSR_OTAT_EN)
+		*enabled = 1;
+
+	/* Setup address space */
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A16)
+		*aspace |= VME_A16;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A24)
+		*aspace |= VME_A24;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A32)
+		*aspace |= VME_A32;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A64)
+		*aspace |= VME_A64;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_CRCSR)
+		*aspace |= VME_CRCSR;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER1)
+		*aspace |= VME_USER1;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER2)
+		*aspace |= VME_USER2;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER3)
+		*aspace |= VME_USER3;
+	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER4)
+		*aspace |= VME_USER4;
+
+	/* Setup 2eSST speeds */
+	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_160)
+		*cycle |= VME_2eSST160;
+	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_267)
+		*cycle |= VME_2eSST267;
+	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_320)
+		*cycle |= VME_2eSST320;
+
+	/* Setup cycle types */
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_SCT)
+		*cycle |= VME_SCT;
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_BLT)
+		*cycle |= VME_BLT;
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_MBLT)
+		*cycle |= VME_MBLT;
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eVME)
+		*cycle |= VME_2eVME;
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eSST)
+		*cycle |= VME_2eSST;
+	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eSSTB)
+		*cycle |= VME_2eSSTB;
+
+	if (ctl & TSI148_LCSR_OTAT_SUP)
+		*cycle |= VME_SUPER;
+	else
+		*cycle |= VME_USER;
+
+	if (ctl & TSI148_LCSR_OTAT_PGM)
+		*cycle |= VME_PROG;
+	else
+		*cycle |= VME_DATA;
+
+	/* Setup data width */
+	if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_16)
+		*dwidth = VME_D16;
+	if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_32)
+		*dwidth = VME_D32;
+
+	return 0;
+}
+
+
+int tsi148_master_get( struct vme_master_resource *image, int *enabled,
+	unsigned long long *vme_base, unsigned long long *size,
+	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
+{
+	int retval;
+
+	spin_lock(&(image->lock));
+
+	retval = __tsi148_master_get(image, enabled, vme_base, size, aspace,
+		cycle, dwidth);
+
+	spin_unlock(&(image->lock));
+
+	return retval;
+}
+
+ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
+	size_t count, loff_t offset)
+{
+	int retval, enabled;
+	unsigned long long vme_base, size;
+	vme_address_t aspace;
+	vme_cycle_t cycle;
+	vme_width_t dwidth;
+	struct vme_bus_error *vme_err = NULL;
+
+	spin_lock(&(image->lock));
+
+	memcpy_fromio(buf, image->kern_base + offset, (unsigned int)count);
+	retval = count;
+
+	if (!err_chk)
+		goto skip_chk;
+
+	__tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle,
+		&dwidth);
+
+	vme_err = tsi148_find_error(aspace, vme_base + offset, count);
+	if(vme_err != NULL) {
+		dev_err(image->parent->parent, "First VME read error detected "
+			"an at address 0x%llx\n", vme_err->address);
+		retval = vme_err->address - (vme_base + offset);
+		/* Clear down save errors in this address range */
+		tsi148_clear_errors(aspace, vme_base + offset, count);
+	}
+
+skip_chk:
+	spin_unlock(&(image->lock));
+
+	return retval;
+}
+
+
+/* XXX We need to change vme_master_resource->sem to a spinlock so that read
+ *     and write functions can be used in an interrupt context
+ */
+ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
+	size_t count, loff_t offset)
+{
+	int retval = 0, enabled;
+	unsigned long long vme_base, size;
+	vme_address_t aspace;
+	vme_cycle_t cycle;
+	vme_width_t dwidth;
+
+	struct vme_bus_error *vme_err = NULL;
+
+	spin_lock(&(image->lock));
+
+	memcpy_toio(image->kern_base + offset, buf, (unsigned int)count);
+	retval = count;
+
+	/*
+	 * Writes are posted. We need to do a read on the VME bus to flush out
+	 * all of the writes before we check for errors. We can't guarentee
+	 * that reading the data we have just written is safe. It is believed
+	 * that there isn't any read, write re-ordering, so we can read any
+	 * location in VME space, so lets read the Device ID from the tsi148's
+	 * own registers as mapped into CR/CSR space.
+	 *
+	 * We check for saved errors in the written address range/space.
+	 */
+
+	if (!err_chk)
+		goto skip_chk;
+
+	/*
+	 * Get window info first, to maximise the time that the buffers may
+	 * fluch on their own
+	 */
+	__tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle,
+		&dwidth);
+
+	ioread16(flush_image->kern_base + 0x7F000);
+
+	vme_err = tsi148_find_error(aspace, vme_base + offset, count);
+	if(vme_err != NULL) {
+		printk("First VME write error detected an at address 0x%llx\n",
+			vme_err->address);
+		retval = vme_err->address - (vme_base + offset);
+		/* Clear down save errors in this address range */
+		tsi148_clear_errors(aspace, vme_base + offset, count);
+	}
+
+skip_chk:
+	spin_unlock(&(image->lock));
+
+	return retval;
+}
+
+/*
+ * Perform an RMW cycle on the VME bus.
+ *
+ * Requires a previously configured master window, returns final value.
+ */
+unsigned int tsi148_master_rmw(struct vme_master_resource *image,
+	unsigned int mask, unsigned int compare, unsigned int swap,
+	loff_t offset)
+{
+	unsigned long long pci_addr;
+	unsigned int pci_addr_high, pci_addr_low;
+	u32 tmp, result;
+	int i;
+
+
+	/* Find the PCI address that maps to the desired VME address */
+	i = image->number;
+
+	/* Locking as we can only do one of these at a time */
+	down(&(vme_rmw));
+
+	/* Lock image */
+	spin_lock(&(image->lock));
+
+	pci_addr_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAU);
+	pci_addr_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
+		TSI148_LCSR_OFFSET_OTSAL);
+
+	reg_join(pci_addr_high, pci_addr_low, &pci_addr);
+	reg_split(pci_addr + offset, &pci_addr_high, &pci_addr_low);
+
+	/* Configure registers */
+	iowrite32be(mask, tsi148_bridge->base + TSI148_LCSR_RMWEN);
+	iowrite32be(compare, tsi148_bridge->base + TSI148_LCSR_RMWC);
+	iowrite32be(swap, tsi148_bridge->base + TSI148_LCSR_RMWS);
+	iowrite32be(pci_addr_high, tsi148_bridge->base + TSI148_LCSR_RMWAU);
+	iowrite32be(pci_addr_low, tsi148_bridge->base + TSI148_LCSR_RMWAL);
+
+	/* Enable RMW */
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+	tmp |= TSI148_LCSR_VMCTRL_RMWEN;
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+
+	/* Kick process off with a read to the required address. */
+	result = ioread32be(image->kern_base + offset);
+
+	/* Disable RMW */
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+	tmp &= ~TSI148_LCSR_VMCTRL_RMWEN;
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+
+	spin_unlock(&(image->lock));
+
+	up(&(vme_rmw));
+
+	return result;
+}
+
+static int tsi148_dma_set_vme_src_attributes (u32 *attr, vme_address_t aspace,
+	vme_cycle_t cycle, vme_width_t dwidth)
+{
+	/* Setup 2eSST speeds */
+	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
+	case VME_2eSST160:
+		*attr |= TSI148_LCSR_DSAT_2eSSTM_160;
+		break;
+	case VME_2eSST267:
+		*attr |= TSI148_LCSR_DSAT_2eSSTM_267;
+		break;
+	case VME_2eSST320:
+		*attr |= TSI148_LCSR_DSAT_2eSSTM_320;
+		break;
+	}
+
+	/* Setup cycle types */
+	if (cycle & VME_SCT) {
+		*attr |= TSI148_LCSR_DSAT_TM_SCT;
+	}
+	if (cycle & VME_BLT) {
+		*attr |= TSI148_LCSR_DSAT_TM_BLT;
+	}
+	if (cycle & VME_MBLT) {
+		*attr |= TSI148_LCSR_DSAT_TM_MBLT;
+	}
+	if (cycle & VME_2eVME) {
+		*attr |= TSI148_LCSR_DSAT_TM_2eVME;
+	}
+	if (cycle & VME_2eSST) {
+		*attr |= TSI148_LCSR_DSAT_TM_2eSST;
+	}
+	if (cycle & VME_2eSSTB) {
+		printk("Currently not setting Broadcast Select Registers\n");
+		*attr |= TSI148_LCSR_DSAT_TM_2eSSTB;
+	}
+
+	/* Setup data width */
+	switch (dwidth) {
+	case VME_D16:
+		*attr |= TSI148_LCSR_DSAT_DBW_16;
+		break;
+	case VME_D32:
+		*attr |= TSI148_LCSR_DSAT_DBW_32;
+		break;
+	default:
+		printk("Invalid data width\n");
+		return -EINVAL;
+	}
+
+	/* Setup address space */
+	switch (aspace) {
+	case VME_A16:
+		*attr |= TSI148_LCSR_DSAT_AMODE_A16;
+		break;
+	case VME_A24:
+		*attr |= TSI148_LCSR_DSAT_AMODE_A24;
+		break;
+	case VME_A32:
+		*attr |= TSI148_LCSR_DSAT_AMODE_A32;
+		break;
+	case VME_A64:
+		*attr |= TSI148_LCSR_DSAT_AMODE_A64;
+		break;
+	case VME_CRCSR:
+		*attr |= TSI148_LCSR_DSAT_AMODE_CRCSR;
+		break;
+	case VME_USER1:
+		*attr |= TSI148_LCSR_DSAT_AMODE_USER1;
+		break;
+	case VME_USER2:
+		*attr |= TSI148_LCSR_DSAT_AMODE_USER2;
+		break;
+	case VME_USER3:
+		*attr |= TSI148_LCSR_DSAT_AMODE_USER3;
+		break;
+	case VME_USER4:
+		*attr |= TSI148_LCSR_DSAT_AMODE_USER4;
+		break;
+	default:
+		printk("Invalid address space\n");
+		return -EINVAL;
+		break;
+	}
+
+	if (cycle & VME_SUPER)
+		*attr |= TSI148_LCSR_DSAT_SUP;
+	if (cycle & VME_PROG)
+		*attr |= TSI148_LCSR_DSAT_PGM;
+
+	return 0;
+}
+
+static int tsi148_dma_set_vme_dest_attributes(u32 *attr, vme_address_t aspace,
+	vme_cycle_t cycle, vme_width_t dwidth)
+{
+	/* Setup 2eSST speeds */
+	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
+	case VME_2eSST160:
+		*attr |= TSI148_LCSR_DDAT_2eSSTM_160;
+		break;
+	case VME_2eSST267:
+		*attr |= TSI148_LCSR_DDAT_2eSSTM_267;
+		break;
+	case VME_2eSST320:
+		*attr |= TSI148_LCSR_DDAT_2eSSTM_320;
+		break;
+	}
+
+	/* Setup cycle types */
+	if (cycle & VME_SCT) {
+		*attr |= TSI148_LCSR_DDAT_TM_SCT;
+	}
+	if (cycle & VME_BLT) {
+		*attr |= TSI148_LCSR_DDAT_TM_BLT;
+	}
+	if (cycle & VME_MBLT) {
+		*attr |= TSI148_LCSR_DDAT_TM_MBLT;
+	}
+	if (cycle & VME_2eVME) {
+		*attr |= TSI148_LCSR_DDAT_TM_2eVME;
+	}
+	if (cycle & VME_2eSST) {
+		*attr |= TSI148_LCSR_DDAT_TM_2eSST;
+	}
+	if (cycle & VME_2eSSTB) {
+		printk("Currently not setting Broadcast Select Registers\n");
+		*attr |= TSI148_LCSR_DDAT_TM_2eSSTB;
+	}
+
+	/* Setup data width */
+	switch (dwidth) {
+	case VME_D16:
+		*attr |= TSI148_LCSR_DDAT_DBW_16;
+		break;
+	case VME_D32:
+		*attr |= TSI148_LCSR_DDAT_DBW_32;
+		break;
+	default:
+		printk("Invalid data width\n");
+		return -EINVAL;
+	}
+
+	/* Setup address space */
+	switch (aspace) {
+	case VME_A16:
+		*attr |= TSI148_LCSR_DDAT_AMODE_A16;
+		break;
+	case VME_A24:
+		*attr |= TSI148_LCSR_DDAT_AMODE_A24;
+		break;
+	case VME_A32:
+		*attr |= TSI148_LCSR_DDAT_AMODE_A32;
+		break;
+	case VME_A64:
+		*attr |= TSI148_LCSR_DDAT_AMODE_A64;
+		break;
+	case VME_CRCSR:
+		*attr |= TSI148_LCSR_DDAT_AMODE_CRCSR;
+		break;
+	case VME_USER1:
+		*attr |= TSI148_LCSR_DDAT_AMODE_USER1;
+		break;
+	case VME_USER2:
+		*attr |= TSI148_LCSR_DDAT_AMODE_USER2;
+		break;
+	case VME_USER3:
+		*attr |= TSI148_LCSR_DDAT_AMODE_USER3;
+		break;
+	case VME_USER4:
+		*attr |= TSI148_LCSR_DDAT_AMODE_USER4;
+		break;
+	default:
+		printk("Invalid address space\n");
+		return -EINVAL;
+		break;
+	}
+
+	if (cycle & VME_SUPER)
+		*attr |= TSI148_LCSR_DDAT_SUP;
+	if (cycle & VME_PROG)
+		*attr |= TSI148_LCSR_DDAT_PGM;
+
+	return 0;
+}
+
+/*
+ * Add a link list descriptor to the list
+ *
+ * XXX Need to handle 2eSST Broadcast select bits
+ */
+int tsi148_dma_list_add (struct vme_dma_list *list, struct vme_dma_attr *src,
+	struct vme_dma_attr *dest, size_t count)
+{
+	struct tsi148_dma_entry *entry, *prev;
+	u32 address_high, address_low;
+	struct vme_dma_pattern *pattern_attr;
+	struct vme_dma_pci *pci_attr;
+	struct vme_dma_vme *vme_attr;
+	dma_addr_t desc_ptr;
+	int retval = 0;
+
+	/* XXX descriptor must be aligned on 64-bit boundaries */
+	entry = (struct tsi148_dma_entry *)kmalloc(
+		sizeof(struct tsi148_dma_entry), GFP_KERNEL);
+	if (entry == NULL) {
+		printk("Failed to allocate memory for dma resource "
+			"structure\n");
+		retval = -ENOMEM;
+		goto err_mem;
+	}
+
+	/* Test descriptor alignment */
+	if ((unsigned long)&(entry->descriptor) & 0x7) {
+		printk("Descriptor not aligned to 8 byte boundary as "
+			"required: %p\n", &(entry->descriptor));
+		retval = -EINVAL;
+		goto err_align;
+	}
+
+	/* Given we are going to fill out the structure, we probably don't
+	 * need to zero it, but better safe than sorry for now.
+	 */
+	memset(&(entry->descriptor), 0, sizeof(struct tsi148_dma_descriptor));
+
+	/* Fill out source part */
+	switch (src->type) {
+	case VME_DMA_PATTERN:
+		pattern_attr = (struct vme_dma_pattern *)src->private;
+
+		entry->descriptor.dsal = pattern_attr->pattern;
+		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PAT;
+		/* Default behaviour is 32 bit pattern */
+		if (pattern_attr->type & VME_DMA_PATTERN_BYTE) {
+			entry->descriptor.dsat |= TSI148_LCSR_DSAT_PSZ;
+		}
+		/* It seems that the default behaviour is to increment */
+		if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) == 0) {
+			entry->descriptor.dsat |= TSI148_LCSR_DSAT_NIN;
+		}
+		break;
+	case VME_DMA_PCI:
+		pci_attr = (struct vme_dma_pci *)src->private;
+
+		reg_split((unsigned long long)pci_attr->address, &address_high,
+			&address_low);
+		entry->descriptor.dsau = address_high;
+		entry->descriptor.dsal = address_low;
+		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PCI;
+		break;
+	case VME_DMA_VME:
+		vme_attr = (struct vme_dma_vme *)src->private;
+
+		reg_split((unsigned long long)vme_attr->address, &address_high,
+			&address_low);
+		entry->descriptor.dsau = address_high;
+		entry->descriptor.dsal = address_low;
+		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_VME;
+
+		retval = tsi148_dma_set_vme_src_attributes(
+			&(entry->descriptor.dsat), vme_attr->aspace,
+			vme_attr->cycle, vme_attr->dwidth);
+		if(retval < 0 )
+			goto err_source;
+		break;
+	default:
+		printk("Invalid source type\n");
+		retval = -EINVAL;
+		goto err_source;
+		break;
+	}
+
+	/* Assume last link - this will be over-written by adding another */
+	entry->descriptor.dnlau = 0;
+	entry->descriptor.dnlal = TSI148_LCSR_DNLAL_LLA;
+
+
+	/* Fill out destination part */
+	switch (dest->type) {
+	case VME_DMA_PCI:
+		pci_attr = (struct vme_dma_pci *)dest->private;
+
+		reg_split((unsigned long long)pci_attr->address, &address_high,
+			&address_low);
+		entry->descriptor.ddau = address_high;
+		entry->descriptor.ddal = address_low;
+		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_PCI;
+		break;
+	case VME_DMA_VME:
+		vme_attr = (struct vme_dma_vme *)dest->private;
+
+		reg_split((unsigned long long)vme_attr->address, &address_high,
+			&address_low);
+		entry->descriptor.ddau = address_high;
+		entry->descriptor.ddal = address_low;
+		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_VME;
+
+		retval = tsi148_dma_set_vme_dest_attributes(
+			&(entry->descriptor.ddat), vme_attr->aspace,
+			vme_attr->cycle, vme_attr->dwidth);
+		if(retval < 0 )
+			goto err_dest;
+		break;
+	default:
+		printk("Invalid destination type\n");
+		retval = -EINVAL;
+		goto err_dest;
+		break;
+	}
+
+	/* Fill out count */
+	entry->descriptor.dcnt = (u32)count;
+
+	/* Add to list */
+	list_add_tail(&(entry->list), &(list->entries));
+
+	/* Fill out previous descriptors "Next Address" */
+	if(entry->list.prev != &(list->entries)){
+		prev = list_entry(entry->list.prev, struct tsi148_dma_entry,
+			list);
+		/* We need the bus address for the pointer */
+		desc_ptr = virt_to_bus(&(entry->descriptor));
+		reg_split(desc_ptr, &(prev->descriptor.dnlau),
+			&(prev->descriptor.dnlal));
+	}
+
+	return 0;
+
+err_dest:
+err_source:
+err_align:
+		kfree(entry);
+err_mem:
+	return retval;
+}
+
+/*
+ * Check to see if the provided DMA channel is busy.
+ */
+static int tsi148_dma_busy(int channel)
+{
+	u32 tmp;
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
+		TSI148_LCSR_OFFSET_DSTA);
+
+	if (tmp & TSI148_LCSR_DSTA_BSY)
+		return 0;
+	else
+		return 1;
+
+}
+
+/*
+ * Execute a previously generated link list
+ *
+ * XXX Need to provide control register configuration.
+ */
+int tsi148_dma_list_exec(struct vme_dma_list *list)
+{
+	struct vme_dma_resource *ctrlr;
+	int channel, retval = 0;
+	struct tsi148_dma_entry *entry;
+	dma_addr_t bus_addr;
+	u32 bus_addr_high, bus_addr_low;
+	u32 val, dctlreg = 0;
+#if 0
+	int x;
+#endif
+
+	ctrlr = list->parent;
+
+	down(&(ctrlr->sem));
+
+	channel = ctrlr->number;
+
+	if (! list_empty(&(ctrlr->running))) {
+		/*
+		 * XXX We have an active DMA transfer and currently haven't
+		 *     sorted out the mechanism for "pending" DMA transfers.
+		 *     Return busy.
+		 */
+		/* Need to add to pending here */
+		up(&(ctrlr->sem));
+		return -EBUSY;
+	} else {
+		list_add(&(list->list), &(ctrlr->running));
+	}
+#if 0
+	/* XXX Still todo */
+	for (x = 0; x < 8; x++) {	/* vme block size */
+		if ((32 << x) >= vmeDma->maxVmeBlockSize) {
+			break;
+		}
+	}
+	if (x == 8)
+		x = 7;
+	dctlreg |= (x << 12);
+
+	for (x = 0; x < 8; x++) {	/* pci block size */
+		if ((32 << x) >= vmeDma->maxPciBlockSize) {
+			break;
+		}
+	}
+	if (x == 8)
+		x = 7;
+	dctlreg |= (x << 4);
+
+	if (vmeDma->vmeBackOffTimer) {
+		for (x = 1; x < 8; x++) {	/* vme timer */
+			if ((1 << (x - 1)) >= vmeDma->vmeBackOffTimer) {
+				break;
+			}
+		}
+		if (x == 8)
+			x = 7;
+		dctlreg |= (x << 8);
+	}
+
+	if (vmeDma->pciBackOffTimer) {
+		for (x = 1; x < 8; x++) {	/* pci timer */
+			if ((1 << (x - 1)) >= vmeDma->pciBackOffTimer) {
+				break;
+			}
+		}
+		if (x == 8)
+			x = 7;
+		dctlreg |= (x << 0);
+	}
+#endif
+
+	/* Get first bus address and write into registers */
+	entry = list_first_entry(&(list->entries), struct tsi148_dma_entry,
+		list);
+
+	bus_addr = virt_to_bus(&(entry->descriptor));
+
+	up(&(ctrlr->sem));
+
+	reg_split(bus_addr, &bus_addr_high, &bus_addr_low);
+
+	iowrite32be(bus_addr_high, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU);
+	iowrite32be(bus_addr_low, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL);
+
+	/* Start the operation */
+	iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
+
+	wait_event_interruptible(dma_queue[channel], tsi148_dma_busy(channel));
+	/*
+	 * Read status register, this register is valid until we kick off a
+	 * new transfer.
+	 */
+	val = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
+		TSI148_LCSR_OFFSET_DSTA);
+
+	if (val & TSI148_LCSR_DSTA_VBE) {
+		printk(KERN_ERR "tsi148: DMA Error. DSTA=%08X\n", val);
+		retval = -EIO;
+	}
+
+	/* Remove list from running list */
+	down(&(ctrlr->sem));
+	list_del(&(list->list));
+	up(&(ctrlr->sem));
+
+	return retval;
+}
+
+/*
+ * Clean up a previously generated link list
+ *
+ * We have a separate function, don't assume that the chain can't be reused.
+ */
+int tsi148_dma_list_empty(struct vme_dma_list *list)
+{
+	struct list_head *pos, *temp;
+        struct tsi148_dma_entry *entry;
+
+	/* detach and free each entry */
+	list_for_each_safe(pos, temp, &(list->entries)) {
+		list_del(pos);
+		entry = list_entry(pos, struct tsi148_dma_entry, list);
+		kfree(entry);
+	}
+
+	return (0);
+}
+
+/*
+ * All 4 location monitors reside at the same base - this is therefore a
+ * system wide configuration.
+ *
+ * This does not enable the LM monitor - that should be done when the first
+ * callback is attached and disabled when the last callback is removed.
+ */
+int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace,
+	vme_cycle_t cycle)
+{
+	u32 lm_base_high, lm_base_low, lm_ctl = 0;
+	int i;
+
+	/* Get semaphore */
+	down(&(vme_lm));
+
+	/* If we already have a callback attached, we can't move it! */
+	for (i = 0; i < 4; i++) {
+		if(lm_callback[i] != NULL) {
+			up(&(vme_lm));
+			printk("Location monitor callback attached, can't "
+				"reset\n");
+			return -EBUSY;
+		}
+	}
+
+	switch (aspace) {
+	case VME_A16:
+		lm_ctl |= TSI148_LCSR_LMAT_AS_A16;
+		break;
+	case VME_A24:
+		lm_ctl |= TSI148_LCSR_LMAT_AS_A24;
+		break;
+	case VME_A32:
+		lm_ctl |= TSI148_LCSR_LMAT_AS_A32;
+		break;
+	case VME_A64:
+		lm_ctl |= TSI148_LCSR_LMAT_AS_A64;
+		break;
+	default:
+		up(&(vme_lm));
+		printk("Invalid address space\n");
+		return -EINVAL;
+		break;
+	}
+
+	if (cycle & VME_SUPER)
+		lm_ctl |= TSI148_LCSR_LMAT_SUPR ;
+	if (cycle & VME_USER)
+		lm_ctl |= TSI148_LCSR_LMAT_NPRIV;
+	if (cycle & VME_PROG)
+		lm_ctl |= TSI148_LCSR_LMAT_PGM;
+	if (cycle & VME_DATA)
+		lm_ctl |= TSI148_LCSR_LMAT_DATA;
+
+	reg_split(lm_base, &lm_base_high, &lm_base_low);
+
+	iowrite32be(lm_base_high, tsi148_bridge->base + TSI148_LCSR_LMBAU);
+	iowrite32be(lm_base_low, tsi148_bridge->base + TSI148_LCSR_LMBAL);
+	iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
+
+	up(&(vme_lm));
+
+	return 0;
+}
+
+/* Get configuration of the callback monitor and return whether it is enabled
+ * or disabled.
+ */
+int tsi148_lm_get(unsigned long long *lm_base, vme_address_t *aspace,
+	vme_cycle_t *cycle)
+{
+	u32 lm_base_high, lm_base_low, lm_ctl, enabled = 0;
+
+	/* Get semaphore */
+	down(&(vme_lm));
+
+	lm_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAU);
+	lm_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAL);
+	lm_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
+
+	reg_join(lm_base_high, lm_base_low, lm_base);
+
+	if (lm_ctl & TSI148_LCSR_LMAT_EN)
+		enabled = 1;
+
+	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A16) {
+		*aspace |= VME_A16;
+	}
+	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A24) {
+		*aspace |= VME_A24;
+	}
+	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A32) {
+		*aspace |= VME_A32;
+	}
+	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A64) {
+		*aspace |= VME_A64;
+	}
+
+	if (lm_ctl & TSI148_LCSR_LMAT_SUPR)
+		*cycle |= VME_SUPER;
+	if (lm_ctl & TSI148_LCSR_LMAT_NPRIV)
+		*cycle |= VME_USER;
+	if (lm_ctl & TSI148_LCSR_LMAT_PGM)
+		*cycle |= VME_PROG;
+	if (lm_ctl & TSI148_LCSR_LMAT_DATA)
+		*cycle |= VME_DATA;
+
+	up(&(vme_lm));
+
+	return enabled;
+}
+
+/*
+ * Attach a callback to a specific location monitor.
+ *
+ * Callback will be passed the monitor triggered.
+ */
+int tsi148_lm_attach(int monitor, void (*callback)(int))
+{
+	u32 lm_ctl, tmp;
+
+	/* Get semaphore */
+	down(&(vme_lm));
+
+	/* Ensure that the location monitor is configured - need PGM or DATA */
+	lm_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
+	if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) == 0) {
+		up(&(vme_lm));
+		printk("Location monitor not properly configured\n");
+		return -EINVAL;
+	}
+
+	/* Check that a callback isn't already attached */
+	if (lm_callback[monitor] != NULL) {
+		up(&(vme_lm));
+		printk("Existing callback attached\n");
+		return -EBUSY;
+	}
+
+	/* Attach callback */
+	lm_callback[monitor] = callback;
+
+	/* Enable Location Monitor interrupt */
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
+	tmp |= TSI148_LCSR_INTEN_LMEN[monitor];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+	tmp |= TSI148_LCSR_INTEO_LMEO[monitor];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
+
+	/* Ensure that global Location Monitor Enable set */
+	if ((lm_ctl & TSI148_LCSR_LMAT_EN) == 0) {
+		lm_ctl |= TSI148_LCSR_LMAT_EN;
+		iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
+	}
+
+	up(&(vme_lm));
+
+	return 0;
+}
+
+/*
+ * Detach a callback function forn a specific location monitor.
+ */
+int tsi148_lm_detach(int monitor)
+{
+	u32 lm_en, tmp;
+
+	/* Get semaphore */
+	down(&(vme_lm));
+
+	/* Disable Location Monitor and ensure previous interrupts are clear */
+	lm_en = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
+	lm_en &= ~TSI148_LCSR_INTEN_LMEN[monitor];
+	iowrite32be(lm_en, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+	tmp &= ~TSI148_LCSR_INTEO_LMEO[monitor];
+	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
+
+	iowrite32be(TSI148_LCSR_INTC_LMC[monitor],
+		 tsi148_bridge->base + TSI148_LCSR_INTEO);
+
+	/* Detach callback */
+	lm_callback[monitor] = NULL;
+
+	/* If all location monitors disabled, disable global Location Monitor */
+	if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S |
+			TSI148_LCSR_INTS_LM2S | TSI148_LCSR_INTS_LM3S)) == 0) {
+		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
+		tmp &= ~TSI148_LCSR_LMAT_EN;
+		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_LMAT);
+	}
+
+	up(&(vme_lm));
+
+	return 0;
+}
+
+/*
+ * Determine Geographical Addressing
+ */
+int tsi148_slot_get(void)
+{
+        u32 slot = 0;
+
+	slot = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
+	slot = slot & TSI148_LCSR_VSTAT_GA_M;
+	return (int)slot;
+}
+
+static int __init tsi148_init(void)
+{
+	return pci_register_driver(&tsi148_driver);
+}
+
+/*
+ * Configure CR/CSR space
+ *
+ * Access to the CR/CSR can be configured at power-up. The location of the
+ * CR/CSR registers in the CR/CSR address space is determined by the boards
+ * Auto-ID or Geographic address. This function ensures that the window is
+ * enabled at an offset consistent with the boards geopgraphic address.
+ *
+ * Each board has a 512kB window, with the highest 4kB being used for the
+ * boards registers, this means there is a fix length 508kB window which must
+ * be mapped onto PCI memory.
+ */
+static int tsi148_crcsr_init(struct pci_dev *pdev)
+{
+	u32 cbar, crat, vstat;
+	u32 crcsr_bus_high, crcsr_bus_low;
+	int retval;
+
+	/* Allocate mem for CR/CSR image */
+	crcsr_kernel = pci_alloc_consistent(pdev, VME_CRCSR_BUF_SIZE,
+		&crcsr_bus);
+	if (crcsr_kernel == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory for CR/CSR "
+			"image\n");
+		return -ENOMEM;
+	}
+
+	memset(crcsr_kernel, 0, VME_CRCSR_BUF_SIZE);
+
+	reg_split(crcsr_bus, &crcsr_bus_high, &crcsr_bus_low);
+
+	iowrite32be(crcsr_bus_high, tsi148_bridge->base + TSI148_LCSR_CROU);
+	iowrite32be(crcsr_bus_low, tsi148_bridge->base + TSI148_LCSR_CROL);
+
+	/* Ensure that the CR/CSR is configured at the correct offset */
+	cbar = ioread32be(tsi148_bridge->base + TSI148_CBAR);
+	cbar = (cbar & TSI148_CRCSR_CBAR_M)>>3;
+
+	vstat = tsi148_slot_get();
+
+	if (cbar != vstat) {
+		dev_info(&pdev->dev, "Setting CR/CSR offset\n");
+		iowrite32be(cbar<<3, tsi148_bridge->base + TSI148_CBAR);
+	}
+	dev_info(&pdev->dev, "CR/CSR Offset: %d\n", cbar);
+
+	crat = ioread32be(tsi148_bridge->base + TSI148_LCSR_CRAT);
+	if (crat & TSI148_LCSR_CRAT_EN) {
+		dev_info(&pdev->dev, "Enabling CR/CSR space\n");
+		iowrite32be(crat | TSI148_LCSR_CRAT_EN,
+			tsi148_bridge->base + TSI148_LCSR_CRAT);
+	} else
+		dev_info(&pdev->dev, "CR/CSR already enabled\n");
+
+	/* If we want flushed, error-checked writes, set up a window
+	 * over the CR/CSR registers. We read from here to safely flush
+	 * through VME writes.
+	 */
+	if(err_chk) {
+		retval = tsi148_master_set(flush_image, 1, (vstat * 0x80000),
+			0x80000, VME_CRCSR, VME_SCT, VME_D16);
+		if (retval)
+			dev_err(&pdev->dev, "Configuring flush image failed\n");
+	}
+
+	return 0;
+
+}
+
+static void tsi148_crcsr_exit(struct pci_dev *pdev)
+{
+	u32 crat;
+
+	/* Turn off CR/CSR space */
+	crat = ioread32be(tsi148_bridge->base + TSI148_LCSR_CRAT);
+	iowrite32be(crat & ~TSI148_LCSR_CRAT_EN,
+		tsi148_bridge->base + TSI148_LCSR_CRAT);
+
+	/* Free image */
+	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CROU);
+	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CROL);
+
+	pci_free_consistent(pdev, VME_CRCSR_BUF_SIZE, crcsr_kernel, crcsr_bus);
+}
+
+static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int retval, i, master_num;
+	u32 data;
+	struct list_head *pos = NULL;
+	struct vme_master_resource *master_image;
+	struct vme_slave_resource *slave_image;
+	struct vme_dma_resource *dma_ctrlr;
+
+	/* If we want to support more than one of each bridge, we need to
+	 * dynamically generate this so we get one per device
+	 */
+	tsi148_bridge = (struct vme_bridge *)kmalloc(sizeof(struct vme_bridge),
+		GFP_KERNEL);
+	if (tsi148_bridge == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory for device "
+			"structure\n");
+		retval = -ENOMEM;
+		goto err_struct;
+	}
+
+	memset(tsi148_bridge, 0, sizeof(struct vme_bridge));
+
+	/* Enable the device */
+	retval = pci_enable_device(pdev);
+	if (retval) {
+		dev_err(&pdev->dev, "Unable to enable device\n");
+		goto err_enable;
+	}
+
+	/* Map Registers */
+	retval = pci_request_regions(pdev, driver_name);
+	if (retval) {
+		dev_err(&pdev->dev, "Unable to reserve resources\n");
+		goto err_resource;
+	}
+
+	/* map registers in BAR 0 */
+	tsi148_bridge->base = ioremap_nocache(pci_resource_start(pdev, 0), 4096);
+	if (!tsi148_bridge->base) {
+		dev_err(&pdev->dev, "Unable to remap CRG region\n");
+		retval = -EIO;
+		goto err_remap;
+	}
+
+	/* Check to see if the mapping worked out */
+	data = ioread32(tsi148_bridge->base + TSI148_PCFS_ID) & 0x0000FFFF;
+	if (data != PCI_VENDOR_ID_TUNDRA) {
+		dev_err(&pdev->dev, "CRG region check failed\n");
+		retval = -EIO;
+		goto err_test;
+	}
+
+	/* Initialize wait queues & mutual exclusion flags */
+	/* XXX These need to be moved to the vme_bridge structure */
+	init_waitqueue_head(&dma_queue[0]);
+	init_waitqueue_head(&dma_queue[1]);
+	init_waitqueue_head(&iack_queue);
+	init_MUTEX(&(vme_int));
+	init_MUTEX(&(vme_irq));
+	init_MUTEX(&(vme_rmw));
+	init_MUTEX(&(vme_lm));
+
+	tsi148_bridge->parent = &(pdev->dev);
+	strcpy(tsi148_bridge->name, driver_name);
+
+	/* Setup IRQ */
+	retval = tsi148_irq_init(tsi148_bridge);
+	if (retval != 0) {
+		dev_err(&pdev->dev, "Chip Initialization failed.\n");
+		goto err_irq;
+	}
+
+	/* If we are going to flush writes, we need to read from the VME bus.
+	 * We need to do this safely, thus we read the devices own CR/CSR
+	 * register. To do this we must set up a window in CR/CSR space and
+	 * hence have one less master window resource available.
+	 */
+	master_num = TSI148_MAX_MASTER;
+	if(err_chk){
+		master_num--;
+		/* XXX */
+		flush_image = (struct vme_master_resource *)kmalloc(
+			sizeof(struct vme_master_resource), GFP_KERNEL);
+		if (flush_image == NULL) {
+			dev_err(&pdev->dev, "Failed to allocate memory for "
+			"flush resource structure\n");
+			retval = -ENOMEM;
+			goto err_master;
+		}
+		flush_image->parent = tsi148_bridge;
+		spin_lock_init(&(flush_image->lock));
+		flush_image->locked = 1;
+		flush_image->number = master_num;
+		flush_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
+			VME_A64;
+		flush_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
+			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
+			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
+			VME_PROG | VME_DATA;
+		flush_image->width_attr = VME_D16 | VME_D32;
+		memset(&(flush_image->pci_resource), 0,
+			sizeof(struct resource));
+		flush_image->kern_base  = NULL;
+	}
+
+	/* Add master windows to list */
+	INIT_LIST_HEAD(&(tsi148_bridge->master_resources));
+	for (i = 0; i < master_num; i++) {
+		master_image = (struct vme_master_resource *)kmalloc(
+			sizeof(struct vme_master_resource), GFP_KERNEL);
+		if (master_image == NULL) {
+			dev_err(&pdev->dev, "Failed to allocate memory for "
+			"master resource structure\n");
+			retval = -ENOMEM;
+			goto err_master;
+		}
+		master_image->parent = tsi148_bridge;
+		spin_lock_init(&(master_image->lock));
+		master_image->locked = 0;
+		master_image->number = i;
+		master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
+			VME_A64;
+		master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
+			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
+			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
+			VME_PROG | VME_DATA;
+		master_image->width_attr = VME_D16 | VME_D32;
+		memset(&(master_image->pci_resource), 0,
+			sizeof(struct resource));
+		master_image->kern_base  = NULL;
+		list_add_tail(&(master_image->list),
+			&(tsi148_bridge->master_resources));
+	}
+
+	/* Add slave windows to list */
+	INIT_LIST_HEAD(&(tsi148_bridge->slave_resources));
+	for (i = 0; i < TSI148_MAX_SLAVE; i++) {
+		slave_image = (struct vme_slave_resource *)kmalloc(
+			sizeof(struct vme_slave_resource), GFP_KERNEL);
+		if (slave_image == NULL) {
+			dev_err(&pdev->dev, "Failed to allocate memory for "
+			"slave resource structure\n");
+			retval = -ENOMEM;
+			goto err_slave;
+		}
+		slave_image->parent = tsi148_bridge;
+		init_MUTEX(&(slave_image->sem));
+		slave_image->locked = 0;
+		slave_image->number = i;
+		slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
+			VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
+			VME_USER3 | VME_USER4;
+		slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
+			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
+			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
+			VME_PROG | VME_DATA;
+		list_add_tail(&(slave_image->list),
+			&(tsi148_bridge->slave_resources));
+	}
+
+	/* Add dma engines to list */
+	INIT_LIST_HEAD(&(tsi148_bridge->dma_resources));
+	for (i = 0; i < TSI148_MAX_DMA; i++) {
+		dma_ctrlr = (struct vme_dma_resource *)kmalloc(
+			sizeof(struct vme_dma_resource), GFP_KERNEL);
+		if (dma_ctrlr == NULL) {
+			dev_err(&pdev->dev, "Failed to allocate memory for "
+			"dma resource structure\n");
+			retval = -ENOMEM;
+			goto err_dma;
+		}
+		dma_ctrlr->parent = tsi148_bridge;
+		init_MUTEX(&(dma_ctrlr->sem));
+		dma_ctrlr->locked = 0;
+		dma_ctrlr->number = i;
+		INIT_LIST_HEAD(&(dma_ctrlr->pending));
+		INIT_LIST_HEAD(&(dma_ctrlr->running));
+		list_add_tail(&(dma_ctrlr->list),
+			&(tsi148_bridge->dma_resources));
+	}
+
+	tsi148_bridge->slave_get = tsi148_slave_get;
+	tsi148_bridge->slave_set = tsi148_slave_set;
+	tsi148_bridge->master_get = tsi148_master_get;
+	tsi148_bridge->master_set = tsi148_master_set;
+	tsi148_bridge->master_read = tsi148_master_read;
+	tsi148_bridge->master_write = tsi148_master_write;
+	tsi148_bridge->master_rmw = tsi148_master_rmw;
+	tsi148_bridge->dma_list_add = tsi148_dma_list_add;
+	tsi148_bridge->dma_list_exec = tsi148_dma_list_exec;
+	tsi148_bridge->dma_list_empty = tsi148_dma_list_empty;
+	tsi148_bridge->request_irq = tsi148_request_irq;
+	tsi148_bridge->free_irq = tsi148_free_irq;
+	tsi148_bridge->generate_irq = tsi148_generate_irq;
+	tsi148_bridge->lm_set = tsi148_lm_set;
+	tsi148_bridge->lm_get = tsi148_lm_get;
+	tsi148_bridge->lm_attach = tsi148_lm_attach;
+	tsi148_bridge->lm_detach = tsi148_lm_detach;
+	tsi148_bridge->slot_get = tsi148_slot_get;
+
+	data = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
+	dev_info(&pdev->dev, "Board is%s the VME system controller\n",
+		(data & TSI148_LCSR_VSTAT_SCONS)? "" : " not");
+	dev_info(&pdev->dev, "VME geographical address is %d\n",
+		data & TSI148_LCSR_VSTAT_GA_M);
+	dev_info(&pdev->dev, "VME Write and flush and error check is %s\n",
+		err_chk ? "enabled" : "disabled");
+
+	if(tsi148_crcsr_init(pdev)) {
+		dev_err(&pdev->dev, "CR/CSR configuration failed.\n");
+		goto err_crcsr;
+
+	}
+
+	/* Need to save tsi148_bridge pointer locally in link list for use in
+	 * tsi148_remove()
+	 */
+	retval = vme_register_bridge(tsi148_bridge);
+	if (retval != 0) {
+		dev_err(&pdev->dev, "Chip Registration failed.\n");
+		goto err_reg;
+	}
+
+	/* Clear VME bus "board fail", and "power-up reset" lines */
+	data = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
+	data &= ~TSI148_LCSR_VSTAT_BRDFL;
+	data |= TSI148_LCSR_VSTAT_CPURST;
+	iowrite32be(data, tsi148_bridge->base + TSI148_LCSR_VSTAT);
+
+	return 0;
+
+	vme_unregister_bridge(tsi148_bridge);
+err_reg:
+	tsi148_crcsr_exit(pdev);
+err_crcsr:
+err_dma:
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->dma_resources)) {
+		dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
+		list_del(pos);
+		kfree(dma_ctrlr);
+	}
+err_slave:
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->slave_resources)) {
+		slave_image = list_entry(pos, struct vme_slave_resource, list);
+		list_del(pos);
+		kfree(slave_image);
+	}
+err_master:
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->master_resources)) {
+		master_image = list_entry(pos, struct vme_master_resource,				list);
+		list_del(pos);
+		kfree(master_image);
+	}
+
+	tsi148_irq_exit(pdev);
+err_irq:
+err_test:
+	iounmap(tsi148_bridge->base);
+err_remap:
+	pci_release_regions(pdev);
+err_resource:
+	pci_disable_device(pdev);
+err_enable:
+	kfree(tsi148_bridge);
+err_struct:
+	return retval;
+
+}
+
+static void tsi148_remove(struct pci_dev *pdev)
+{
+	struct list_head *pos = NULL;
+	struct vme_master_resource *master_image;
+	struct vme_slave_resource *slave_image;
+	struct vme_dma_resource *dma_ctrlr;
+	int i;
+
+	dev_dbg(&pdev->dev, "Driver is being unloaded.\n");
+
+	/* XXX We need to find the pdev->dev in the list of vme_bridge->dev's */
+
+	/*
+	 *  Shutdown all inbound and outbound windows.
+	 */
+	for (i = 0; i < 8; i++) {
+		iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_IT[i] +
+			TSI148_LCSR_OFFSET_ITAT);
+		iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_OT[i] +
+			TSI148_LCSR_OFFSET_OTAT);
+	}
+
+	/*
+	 *  Shutdown Location monitor.
+	 */
+	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_LMAT);
+
+	/*
+	 *  Shutdown CRG map.
+	 */
+	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CSRAT);
+
+	/*
+	 *  Clear error status.
+	 */
+	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_EDPAT);
+	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_VEAT);
+	iowrite32be(0x07000700, tsi148_bridge->base + TSI148_LCSR_PSTAT);
+
+	/*
+	 *  Remove VIRQ interrupt (if any)
+	 */
+	if (ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR) & 0x800) {
+		iowrite32be(0x8000, tsi148_bridge->base + TSI148_LCSR_VICR);
+	}
+
+	/*
+	 *  Disable and clear all interrupts.
+	 */
+	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEO);
+	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTC);
+	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+	/*
+	 *  Map all Interrupts to PCI INTA
+	 */
+	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTM1);
+	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTM2);
+
+	tsi148_irq_exit(pdev);
+
+	vme_unregister_bridge(tsi148_bridge);
+
+	tsi148_crcsr_exit(pdev);
+
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->dma_resources)) {
+		dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
+		list_del(pos);
+		kfree(dma_ctrlr);
+	}
+
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->slave_resources)) {
+		slave_image = list_entry(pos, struct vme_slave_resource, list);
+		list_del(pos);
+		kfree(slave_image);
+	}
+
+	/* resources are stored in link list */
+	list_for_each(pos, &(tsi148_bridge->master_resources)) {
+		master_image = list_entry(pos, struct vme_master_resource,				list);
+		list_del(pos);
+		kfree(master_image);
+	}
+
+	tsi148_irq_exit(pdev);
+
+	iounmap(tsi148_bridge->base);
+
+	pci_release_regions(pdev);
+
+	pci_disable_device(pdev);
+
+	kfree(tsi148_bridge);
+}
+
+static void __exit tsi148_exit(void)
+{
+	pci_unregister_driver(&tsi148_driver);
+
+	printk(KERN_DEBUG "Driver removed.\n");
+}
+
+MODULE_PARM_DESC(err_chk, "Check for VME errors on reads and writes");
+module_param(err_chk, bool, 0);
+
+MODULE_DESCRIPTION("VME driver for the Tundra Tempe VME bridge");
+MODULE_LICENSE("GPL");
+
+module_init(tsi148_init);
+module_exit(tsi148_exit);
+
+/*----------------------------------------------------------------------------
+ * STAGING
+ *--------------------------------------------------------------------------*/
+
+#if 0
+/*
+ * Direct Mode DMA transfer
+ *
+ * XXX Not looking at direct mode for now, we can always use link list mode
+ *     with a single entry.
+ */
+int tsi148_dma_run(struct vme_dma_resource *resource, struct vme_dma_attr src,
+	struct vme_dma_attr dest, size_t count)
+{
+	u32 dctlreg = 0;
+	unsigned int tmp;
+	int val;
+	int channel, x;
+	struct vmeDmaPacket *cur_dma;
+	struct tsi148_dma_descriptor *dmaLL;
+
+	/* direct mode */
+	dctlreg = 0x800000;
+
+	for (x = 0; x < 8; x++) {	/* vme block size */
+		if ((32 << x) >= vmeDma->maxVmeBlockSize) {
+			break;
+		}
+	}
+	if (x == 8)
+		x = 7;
+	dctlreg |= (x << 12);
+
+	for (x = 0; x < 8; x++) {	/* pci block size */
+		if ((32 << x) >= vmeDma->maxPciBlockSize) {
+			break;
+		}
+	}
+	if (x == 8)
+		x = 7;
+	dctlreg |= (x << 4);
+
+	if (vmeDma->vmeBackOffTimer) {
+		for (x = 1; x < 8; x++) {	/* vme timer */
+			if ((1 << (x - 1)) >= vmeDma->vmeBackOffTimer) {
+				break;
+			}
+		}
+		if (x == 8)
+			x = 7;
+		dctlreg |= (x << 8);
+	}
+
+	if (vmeDma->pciBackOffTimer) {
+		for (x = 1; x < 8; x++) {	/* pci timer */
+			if ((1 << (x - 1)) >= vmeDma->pciBackOffTimer) {
+				break;
+			}
+		}
+		if (x == 8)
+			x = 7;
+		dctlreg |= (x << 0);
+	}
+
+	/* Program registers for DMA transfer */
+	iowrite32be(dmaLL->dsau, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAU);
+	iowrite32be(dmaLL->dsal, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAL);
+	iowrite32be(dmaLL->ddau, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAU);
+	iowrite32be(dmaLL->ddal, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAL);
+	iowrite32be(dmaLL->dsat, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAT);
+	iowrite32be(dmaLL->ddat, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAT);
+	iowrite32be(dmaLL->dcnt, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCNT);
+	iowrite32be(dmaLL->ddbs, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDBS);
+
+	/* Start the operation */
+	iowrite32be(dctlreg | 0x2000000, tsi148_bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
+
+	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
+		TSI148_LCSR_OFFSET_DSTA);
+	wait_event_interruptible(dma_queue[channel], (tmp & 0x1000000) == 0);
+
+	/*
+	 * Read status register, we should probably do this in some error
+	 * handler rather than here so that we can be sure we haven't kicked off
+	 * another DMA transfer.
+	 */
+	val = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
+		TSI148_LCSR_OFFSET_DSTA);
+
+	vmeDma->vmeDmaStatus = 0;
+	if (val & 0x10000000) {
+		printk(KERN_ERR
+			"DMA Error in DMA_tempe_irqhandler DSTA=%08X\n",
+			val);
+		vmeDma->vmeDmaStatus = val;
+
+	}
+	return (0);
+}
+#endif
+
+#if 0
+
+/* Global VME controller information */
+struct pci_dev *vme_pci_dev;
+
+/*
+ * Set the VME bus arbiter with the requested attributes
+ */
+int tempe_set_arbiter(vmeArbiterCfg_t * vmeArb)
+{
+	int temp_ctl = 0;
+	int gto = 0;
+
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VCTRL);
+	temp_ctl &= 0xFFEFFF00;
+
+	if (vmeArb->globalTimeoutTimer == 0xFFFFFFFF) {
+		gto = 8;
+	} else if (vmeArb->globalTimeoutTimer > 2048) {
+		return (-EINVAL);
+	} else if (vmeArb->globalTimeoutTimer == 0) {
+		gto = 0;
+	} else {
+		gto = 1;
+		while ((16 * (1 << (gto - 1))) < vmeArb->globalTimeoutTimer) {
+			gto += 1;
+		}
+	}
+	temp_ctl |= gto;
+
+	if (vmeArb->arbiterMode != VME_PRIORITY_MODE) {
+		temp_ctl |= 1 << 6;
+	}
+
+	if (vmeArb->arbiterTimeoutFlag) {
+		temp_ctl |= 1 << 7;
+	}
+
+	if (vmeArb->noEarlyReleaseFlag) {
+		temp_ctl |= 1 << 20;
+	}
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_VCTRL);
+
+	return (0);
+}
+
+/*
+ * Return the attributes of the VME bus arbiter.
+ */
+int tempe_get_arbiter(vmeArbiterCfg_t * vmeArb)
+{
+	int temp_ctl = 0;
+	int gto = 0;
+
+
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VCTRL);
+
+	gto = temp_ctl & 0xF;
+	if (gto != 0) {
+		vmeArb->globalTimeoutTimer = (16 * (1 << (gto - 1)));
+	}
+
+	if (temp_ctl & (1 << 6)) {
+		vmeArb->arbiterMode = VME_R_ROBIN_MODE;
+	} else {
+		vmeArb->arbiterMode = VME_PRIORITY_MODE;
+	}
+
+	if (temp_ctl & (1 << 7)) {
+		vmeArb->arbiterTimeoutFlag = 1;
+	}
+
+	if (temp_ctl & (1 << 20)) {
+		vmeArb->noEarlyReleaseFlag = 1;
+	}
+
+	return (0);
+}
+
+/*
+ * Set the VME bus requestor with the requested attributes
+ */
+int tempe_set_requestor(vmeRequesterCfg_t * vmeReq)
+{
+	int temp_ctl = 0;
+
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+	temp_ctl &= 0xFFFF0000;
+
+	if (vmeReq->releaseMode == 1) {
+		temp_ctl |= (1 << 3);
+	}
+
+	if (vmeReq->fairMode == 1) {
+		temp_ctl |= (1 << 2);
+	}
+
+	temp_ctl |= (vmeReq->timeonTimeoutTimer & 7) << 8;
+	temp_ctl |= (vmeReq->timeoffTimeoutTimer & 7) << 12;
+	temp_ctl |= vmeReq->requestLevel;
+
+	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+	return (0);
+}
+
+/*
+ * Return the attributes of the VME bus requestor
+ */
+int tempe_get_requestor(vmeRequesterCfg_t * vmeReq)
+{
+	int temp_ctl = 0;
+
+	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
+
+	if (temp_ctl & 0x18) {
+		vmeReq->releaseMode = 1;
+	}
+
+	if (temp_ctl & (1 << 2)) {
+		vmeReq->fairMode = 1;
+	}
+
+	vmeReq->requestLevel = temp_ctl & 3;
+	vmeReq->timeonTimeoutTimer = (temp_ctl >> 8) & 7;
+	vmeReq->timeoffTimeoutTimer = (temp_ctl >> 12) & 7;
+
+	return (0);
+}
+
+
+#endif
--- /dev/null
+++ b/drivers/staging/vme/bridges/vme_tsi148.h
@@ -0,0 +1,1387 @@
+/*
+ * tsi148.h
+ *
+ * Support for the Tundra TSI148 VME Bridge chip
+ *
+ * Author: Tom Armistead
+ * Updated and maintained by Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef TSI148_H
+#define TSI148_H
+
+#ifndef	PCI_VENDOR_ID_TUNDRA
+#define	PCI_VENDOR_ID_TUNDRA 0x10e3
+#endif
+
+#ifndef	PCI_DEVICE_ID_TUNDRA_TSI148
+#define	PCI_DEVICE_ID_TUNDRA_TSI148 0x148
+#endif
+
+/*
+ *  Define the number of each that the Tsi148 supports.
+ */
+#define TSI148_MAX_MASTER		8	/* Max Master Windows */
+#define TSI148_MAX_SLAVE		8	/* Max Slave Windows */
+#define TSI148_MAX_DMA			2	/* Max DMA Controllers */
+#define TSI148_MAX_MAILBOX		4	/* Max Mail Box registers */
+#define TSI148_MAX_SEMAPHORE		8	/* Max Semaphores */
+
+/*
+ * Layout of a DMAC Linked-List Descriptor
+ *
+ * Note: This structure is accessed via the chip and therefore must be
+ *       correctly laid out - It must also be aligned on 64-bit boundaries.
+ */
+struct tsi148_dma_descriptor {
+	u32 dsau;      /* Source Address */
+	u32 dsal;
+	u32 ddau;      /* Destination Address */
+	u32 ddal;
+	u32 dsat;      /* Source attributes */
+	u32 ddat;      /* Destination attributes */
+	u32 dnlau;     /* Next link address */
+	u32 dnlal;
+	u32 dcnt;      /* Byte count */
+	u32 ddbs;      /* 2eSST Broadcast select */
+};
+
+struct tsi148_dma_entry {
+	/*
+	 * The descriptor needs to be aligned on a 64-bit boundary, we increase
+	 * the chance of this by putting it first in the structure.
+	 */
+	struct tsi148_dma_descriptor descriptor;
+	struct list_head list;
+};
+
+/*
+ *  TSI148 ASIC register structure overlays and bit field definitions.
+ *
+ *      Note:   Tsi148 Register Group (CRG) consists of the following
+ *              combination of registers:
+ *                      PCFS    - PCI Configuration Space Registers
+ *                      LCSR    - Local Control and Status Registers
+ *                      GCSR    - Global Control and Status Registers
+ *                      CR/CSR  - Subset of Configuration ROM /
+ *                                Control and Status Registers
+ */
+
+
+/*
+ *  Command/Status Registers (CRG + $004)
+ */
+#define TSI148_PCFS_ID			0x0
+#define TSI148_PCFS_CSR			0x4
+#define TSI148_PCFS_CLASS		0x8
+#define TSI148_PCFS_MISC0		0xC
+#define TSI148_PCFS_MBARL		0x10
+#define TSI148_PCFS_MBARU		0x14
+
+#define TSI148_PCFS_SUBID		0x28
+
+#define TSI148_PCFS_CAPP		0x34
+
+#define TSI148_PCFS_MISC1		0x3C
+
+#define TSI148_PCFS_XCAPP		0x40
+#define TSI148_PCFS_XSTAT		0x44
+
+/*
+ * LCSR definitions
+ */
+
+/*
+ *    Outbound Translations
+ */
+#define TSI148_LCSR_OT0_OTSAU		0x100
+#define TSI148_LCSR_OT0_OTSAL		0x104
+#define TSI148_LCSR_OT0_OTEAU		0x108
+#define TSI148_LCSR_OT0_OTEAL		0x10C
+#define TSI148_LCSR_OT0_OTOFU		0x110
+#define TSI148_LCSR_OT0_OTOFL		0x114
+#define TSI148_LCSR_OT0_OTBS		0x118
+#define TSI148_LCSR_OT0_OTAT		0x11C
+
+#define TSI148_LCSR_OT1_OTSAU		0x120
+#define TSI148_LCSR_OT1_OTSAL		0x124
+#define TSI148_LCSR_OT1_OTEAU		0x128
+#define TSI148_LCSR_OT1_OTEAL		0x12C
+#define TSI148_LCSR_OT1_OTOFU		0x130
+#define TSI148_LCSR_OT1_OTOFL		0x134
+#define TSI148_LCSR_OT1_OTBS		0x138
+#define TSI148_LCSR_OT1_OTAT		0x13C
+
+#define TSI148_LCSR_OT2_OTSAU		0x140
+#define TSI148_LCSR_OT2_OTSAL		0x144
+#define TSI148_LCSR_OT2_OTEAU		0x148
+#define TSI148_LCSR_OT2_OTEAL		0x14C
+#define TSI148_LCSR_OT2_OTOFU		0x150
+#define TSI148_LCSR_OT2_OTOFL		0x154
+#define TSI148_LCSR_OT2_OTBS		0x158
+#define TSI148_LCSR_OT2_OTAT		0x15C
+
+#define TSI148_LCSR_OT3_OTSAU		0x160
+#define TSI148_LCSR_OT3_OTSAL		0x164
+#define TSI148_LCSR_OT3_OTEAU		0x168
+#define TSI148_LCSR_OT3_OTEAL		0x16C
+#define TSI148_LCSR_OT3_OTOFU		0x170
+#define TSI148_LCSR_OT3_OTOFL		0x174
+#define TSI148_LCSR_OT3_OTBS		0x178
+#define TSI148_LCSR_OT3_OTAT		0x17C
+
+#define TSI148_LCSR_OT4_OTSAU		0x180
+#define TSI148_LCSR_OT4_OTSAL		0x184
+#define TSI148_LCSR_OT4_OTEAU		0x188
+#define TSI148_LCSR_OT4_OTEAL		0x18C
+#define TSI148_LCSR_OT4_OTOFU		0x190
+#define TSI148_LCSR_OT4_OTOFL		0x194
+#define TSI148_LCSR_OT4_OTBS		0x198
+#define TSI148_LCSR_OT4_OTAT		0x19C
+
+#define TSI148_LCSR_OT5_OTSAU		0x1A0
+#define TSI148_LCSR_OT5_OTSAL		0x1A4
+#define TSI148_LCSR_OT5_OTEAU		0x1A8
+#define TSI148_LCSR_OT5_OTEAL		0x1AC
+#define TSI148_LCSR_OT5_OTOFU		0x1B0
+#define TSI148_LCSR_OT5_OTOFL		0x1B4
+#define TSI148_LCSR_OT5_OTBS		0x1B8
+#define TSI148_LCSR_OT5_OTAT		0x1BC
+
+#define TSI148_LCSR_OT6_OTSAU		0x1C0
+#define TSI148_LCSR_OT6_OTSAL		0x1C4
+#define TSI148_LCSR_OT6_OTEAU		0x1C8
+#define TSI148_LCSR_OT6_OTEAL		0x1CC
+#define TSI148_LCSR_OT6_OTOFU		0x1D0
+#define TSI148_LCSR_OT6_OTOFL		0x1D4
+#define TSI148_LCSR_OT6_OTBS		0x1D8
+#define TSI148_LCSR_OT6_OTAT		0x1DC
+
+#define TSI148_LCSR_OT7_OTSAU		0x1E0
+#define TSI148_LCSR_OT7_OTSAL		0x1E4
+#define TSI148_LCSR_OT7_OTEAU		0x1E8
+#define TSI148_LCSR_OT7_OTEAL		0x1EC
+#define TSI148_LCSR_OT7_OTOFU		0x1F0
+#define TSI148_LCSR_OT7_OTOFL		0x1F4
+#define TSI148_LCSR_OT7_OTBS		0x1F8
+#define TSI148_LCSR_OT7_OTAT		0x1FC
+
+#define TSI148_LCSR_OT0		0x100
+#define TSI148_LCSR_OT1		0x120
+#define TSI148_LCSR_OT2		0x140
+#define TSI148_LCSR_OT3		0x160
+#define TSI148_LCSR_OT4		0x180
+#define TSI148_LCSR_OT5		0x1A0
+#define TSI148_LCSR_OT6		0x1C0
+#define TSI148_LCSR_OT7		0x1E0
+
+static const int TSI148_LCSR_OT[8] = { TSI148_LCSR_OT0, TSI148_LCSR_OT1,
+					 TSI148_LCSR_OT2, TSI148_LCSR_OT3,
+					 TSI148_LCSR_OT4, TSI148_LCSR_OT5,
+					 TSI148_LCSR_OT6, TSI148_LCSR_OT7 };
+
+#define TSI148_LCSR_OFFSET_OTSAU	0x0
+#define TSI148_LCSR_OFFSET_OTSAL	0x4
+#define TSI148_LCSR_OFFSET_OTEAU	0x8
+#define TSI148_LCSR_OFFSET_OTEAL	0xC
+#define TSI148_LCSR_OFFSET_OTOFU	0x10
+#define TSI148_LCSR_OFFSET_OTOFL	0x14
+#define TSI148_LCSR_OFFSET_OTBS		0x18
+#define TSI148_LCSR_OFFSET_OTAT		0x1C
+
+/*
+ * VMEbus interupt ack
+ * offset  200
+ */
+#define TSI148_LCSR_VIACK1	0x204
+#define TSI148_LCSR_VIACK2	0x208
+#define TSI148_LCSR_VIACK3	0x20C
+#define TSI148_LCSR_VIACK4	0x210
+#define TSI148_LCSR_VIACK5	0x214
+#define TSI148_LCSR_VIACK6	0x218
+#define TSI148_LCSR_VIACK7	0x21C
+
+static const int TSI148_LCSR_VIACK[8] = { 0, TSI148_LCSR_VIACK1,
+				TSI148_LCSR_VIACK2, TSI148_LCSR_VIACK3,
+				TSI148_LCSR_VIACK4, TSI148_LCSR_VIACK5,
+				TSI148_LCSR_VIACK6, TSI148_LCSR_VIACK7 };
+
+/*
+ * RMW
+ * offset    220
+ */
+#define TSI148_LCSR_RMWAU	0x220
+#define TSI148_LCSR_RMWAL	0x224
+#define TSI148_LCSR_RMWEN	0x228
+#define TSI148_LCSR_RMWC	0x22C
+#define TSI148_LCSR_RMWS	0x230
+
+/*
+ * VMEbus control
+ * offset    234
+ */
+#define TSI148_LCSR_VMCTRL	0x234
+#define TSI148_LCSR_VCTRL	0x238
+#define TSI148_LCSR_VSTAT	0x23C
+
+/*
+ * PCI status
+ * offset  240
+ */
+#define TSI148_LCSR_PSTAT	0x240
+
+/*
+ * VME filter.
+ * offset  250
+ */
+#define TSI148_LCSR_VMEFL	0x250
+
+	/*
+	 * VME exception.
+	 * offset  260
+ */
+#define TSI148_LCSR_VEAU	0x260
+#define TSI148_LCSR_VEAL	0x264
+#define TSI148_LCSR_VEAT	0x268
+
+	/*
+	 * PCI error
+	 * offset  270
+	 */
+#define TSI148_LCSR_EDPAU	0x270
+#define TSI148_LCSR_EDPAL	0x274
+#define TSI148_LCSR_EDPXA	0x278
+#define TSI148_LCSR_EDPXS	0x27C
+#define TSI148_LCSR_EDPAT	0x280
+
+	/*
+	 * Inbound Translations
+	 * offset  300
+	 */
+#define TSI148_LCSR_IT0_ITSAU		0x300
+#define TSI148_LCSR_IT0_ITSAL		0x304
+#define TSI148_LCSR_IT0_ITEAU		0x308
+#define TSI148_LCSR_IT0_ITEAL		0x30C
+#define TSI148_LCSR_IT0_ITOFU		0x310
+#define TSI148_LCSR_IT0_ITOFL		0x314
+#define TSI148_LCSR_IT0_ITAT		0x318
+
+#define TSI148_LCSR_IT1_ITSAU		0x320
+#define TSI148_LCSR_IT1_ITSAL		0x324
+#define TSI148_LCSR_IT1_ITEAU		0x328
+#define TSI148_LCSR_IT1_ITEAL		0x32C
+#define TSI148_LCSR_IT1_ITOFU		0x330
+#define TSI148_LCSR_IT1_ITOFL		0x334
+#define TSI148_LCSR_IT1_ITAT		0x338
+
+#define TSI148_LCSR_IT2_ITSAU		0x340
+#define TSI148_LCSR_IT2_ITSAL		0x344
+#define TSI148_LCSR_IT2_ITEAU		0x348
+#define TSI148_LCSR_IT2_ITEAL		0x34C
+#define TSI148_LCSR_IT2_ITOFU		0x350
+#define TSI148_LCSR_IT2_ITOFL		0x354
+#define TSI148_LCSR_IT2_ITAT		0x358
+
+#define TSI148_LCSR_IT3_ITSAU		0x360
+#define TSI148_LCSR_IT3_ITSAL		0x364
+#define TSI148_LCSR_IT3_ITEAU		0x368
+#define TSI148_LCSR_IT3_ITEAL		0x36C
+#define TSI148_LCSR_IT3_ITOFU		0x370
+#define TSI148_LCSR_IT3_ITOFL		0x374
+#define TSI148_LCSR_IT3_ITAT		0x378
+
+#define TSI148_LCSR_IT4_ITSAU		0x380
+#define TSI148_LCSR_IT4_ITSAL		0x384
+#define TSI148_LCSR_IT4_ITEAU		0x388
+#define TSI148_LCSR_IT4_ITEAL		0x38C
+#define TSI148_LCSR_IT4_ITOFU		0x390
+#define TSI148_LCSR_IT4_ITOFL		0x394
+#define TSI148_LCSR_IT4_ITAT		0x398
+
+#define TSI148_LCSR_IT5_ITSAU		0x3A0
+#define TSI148_LCSR_IT5_ITSAL		0x3A4
+#define TSI148_LCSR_IT5_ITEAU		0x3A8
+#define TSI148_LCSR_IT5_ITEAL		0x3AC
+#define TSI148_LCSR_IT5_ITOFU		0x3B0
+#define TSI148_LCSR_IT5_ITOFL		0x3B4
+#define TSI148_LCSR_IT5_ITAT		0x3B8
+
+#define TSI148_LCSR_IT6_ITSAU		0x3C0
+#define TSI148_LCSR_IT6_ITSAL		0x3C4
+#define TSI148_LCSR_IT6_ITEAU		0x3C8
+#define TSI148_LCSR_IT6_ITEAL		0x3CC
+#define TSI148_LCSR_IT6_ITOFU		0x3D0
+#define TSI148_LCSR_IT6_ITOFL		0x3D4
+#define TSI148_LCSR_IT6_ITAT		0x3D8
+
+#define TSI148_LCSR_IT7_ITSAU		0x3E0
+#define TSI148_LCSR_IT7_ITSAL		0x3E4
+#define TSI148_LCSR_IT7_ITEAU		0x3E8
+#define TSI148_LCSR_IT7_ITEAL		0x3EC
+#define TSI148_LCSR_IT7_ITOFU		0x3F0
+#define TSI148_LCSR_IT7_ITOFL		0x3F4
+#define TSI148_LCSR_IT7_ITAT		0x3F8
+
+
+#define TSI148_LCSR_IT0		0x300
+#define TSI148_LCSR_IT1		0x320
+#define TSI148_LCSR_IT2		0x340
+#define TSI148_LCSR_IT3		0x360
+#define TSI148_LCSR_IT4		0x380
+#define TSI148_LCSR_IT5		0x3A0
+#define TSI148_LCSR_IT6		0x3C0
+#define TSI148_LCSR_IT7		0x3E0
+
+static const int TSI148_LCSR_IT[8] = { TSI148_LCSR_IT0, TSI148_LCSR_IT1,
+					 TSI148_LCSR_IT2, TSI148_LCSR_IT3,
+					 TSI148_LCSR_IT4, TSI148_LCSR_IT5,
+					 TSI148_LCSR_IT6, TSI148_LCSR_IT7 };
+
+#define TSI148_LCSR_OFFSET_ITSAU	0x0
+#define TSI148_LCSR_OFFSET_ITSAL	0x4
+#define TSI148_LCSR_OFFSET_ITEAU	0x8
+#define TSI148_LCSR_OFFSET_ITEAL	0xC
+#define TSI148_LCSR_OFFSET_ITOFU	0x10
+#define TSI148_LCSR_OFFSET_ITOFL	0x14
+#define TSI148_LCSR_OFFSET_ITAT		0x18
+
+	/*
+	 * Inbound Translation GCSR
+	 * offset  400
+	 */
+#define TSI148_LCSR_GBAU	0x400
+#define TSI148_LCSR_GBAL	0x404
+#define TSI148_LCSR_GCSRAT	0x408
+
+	/*
+	 * Inbound Translation CRG
+	 * offset  40C
+	 */
+#define TSI148_LCSR_CBAU	0x40C
+#define TSI148_LCSR_CBAL	0x410
+#define TSI148_LCSR_CSRAT	0x414
+
+	/*
+	 * Inbound Translation CR/CSR
+	 *         CRG
+	 * offset  418
+	 */
+#define TSI148_LCSR_CROU	0x418
+#define TSI148_LCSR_CROL	0x41C
+#define TSI148_LCSR_CRAT	0x420
+
+	/*
+	 * Inbound Translation Location Monitor
+	 * offset  424
+	 */
+#define TSI148_LCSR_LMBAU	0x424
+#define TSI148_LCSR_LMBAL	0x428
+#define TSI148_LCSR_LMAT	0x42C
+
+	/*
+	 * VMEbus Interrupt Control.
+	 * offset  430
+	 */
+#define TSI148_LCSR_BCU		0x430
+#define TSI148_LCSR_BCL		0x434
+#define TSI148_LCSR_BPGTR	0x438
+#define TSI148_LCSR_BPCTR	0x43C
+#define TSI148_LCSR_VICR	0x440
+
+	/*
+	 * Local Bus Interrupt Control.
+	 * offset  448
+	 */
+#define TSI148_LCSR_INTEN	0x448
+#define TSI148_LCSR_INTEO	0x44C
+#define TSI148_LCSR_INTS	0x450
+#define TSI148_LCSR_INTC	0x454
+#define TSI148_LCSR_INTM1	0x458
+#define TSI148_LCSR_INTM2	0x45C
+
+	/*
+	 * DMA Controllers
+	 * offset 500
+	 */
+#define TSI148_LCSR_DCTL0	0x500
+#define TSI148_LCSR_DSTA0	0x504
+#define TSI148_LCSR_DCSAU0	0x508
+#define TSI148_LCSR_DCSAL0	0x50C
+#define TSI148_LCSR_DCDAU0	0x510
+#define TSI148_LCSR_DCDAL0	0x514
+#define TSI148_LCSR_DCLAU0	0x518
+#define TSI148_LCSR_DCLAL0	0x51C
+#define TSI148_LCSR_DSAU0	0x520
+#define TSI148_LCSR_DSAL0	0x524
+#define TSI148_LCSR_DDAU0	0x528
+#define TSI148_LCSR_DDAL0	0x52C
+#define TSI148_LCSR_DSAT0	0x530
+#define TSI148_LCSR_DDAT0	0x534
+#define TSI148_LCSR_DNLAU0	0x538
+#define TSI148_LCSR_DNLAL0	0x53C
+#define TSI148_LCSR_DCNT0	0x540
+#define TSI148_LCSR_DDBS0	0x544
+
+#define TSI148_LCSR_DCTL1	0x580
+#define TSI148_LCSR_DSTA1	0x584
+#define TSI148_LCSR_DCSAU1	0x588
+#define TSI148_LCSR_DCSAL1	0x58C
+#define TSI148_LCSR_DCDAU1	0x590
+#define TSI148_LCSR_DCDAL1	0x594
+#define TSI148_LCSR_DCLAU1	0x598
+#define TSI148_LCSR_DCLAL1	0x59C
+#define TSI148_LCSR_DSAU1	0x5A0
+#define TSI148_LCSR_DSAL1	0x5A4
+#define TSI148_LCSR_DDAU1	0x5A8
+#define TSI148_LCSR_DDAL1	0x5AC
+#define TSI148_LCSR_DSAT1	0x5B0
+#define TSI148_LCSR_DDAT1	0x5B4
+#define TSI148_LCSR_DNLAU1	0x5B8
+#define TSI148_LCSR_DNLAL1	0x5BC
+#define TSI148_LCSR_DCNT1	0x5C0
+#define TSI148_LCSR_DDBS1	0x5C4
+
+#define TSI148_LCSR_DMA0	0x500
+#define TSI148_LCSR_DMA1	0x580
+
+
+static const int TSI148_LCSR_DMA[TSI148_MAX_DMA] = { TSI148_LCSR_DMA0,
+						TSI148_LCSR_DMA1 };
+
+#define TSI148_LCSR_OFFSET_DCTL		0x0
+#define TSI148_LCSR_OFFSET_DSTA		0x4
+#define TSI148_LCSR_OFFSET_DCSAU	0x8
+#define TSI148_LCSR_OFFSET_DCSAL	0xC
+#define TSI148_LCSR_OFFSET_DCDAU	0x10
+#define TSI148_LCSR_OFFSET_DCDAL	0x14
+#define TSI148_LCSR_OFFSET_DCLAU	0x18
+#define TSI148_LCSR_OFFSET_DCLAL	0x1C
+#define TSI148_LCSR_OFFSET_DSAU		0x20
+#define TSI148_LCSR_OFFSET_DSAL		0x24
+#define TSI148_LCSR_OFFSET_DDAU		0x28
+#define TSI148_LCSR_OFFSET_DDAL		0x2C
+#define TSI148_LCSR_OFFSET_DSAT		0x30
+#define TSI148_LCSR_OFFSET_DDAT		0x34
+#define TSI148_LCSR_OFFSET_DNLAU	0x38
+#define TSI148_LCSR_OFFSET_DNLAL	0x3C
+#define TSI148_LCSR_OFFSET_DCNT		0x40
+#define TSI148_LCSR_OFFSET_DDBS		0x44
+
+	/*
+	 * GCSR Register Group
+	 */
+
+	/*
+	 *         GCSR    CRG
+	 * offset   00     600 - DEVI/VENI
+	 * offset   04     604 - CTRL/GA/REVID
+	 * offset   08     608 - Semaphore3/2/1/0
+	 * offset   0C     60C - Seamphore7/6/5/4
+	 */
+#define TSI148_GCSR_ID		0x600
+#define TSI148_GCSR_CSR		0x604
+#define TSI148_GCSR_SEMA0	0x608
+#define TSI148_GCSR_SEMA1	0x60C
+
+	/*
+	 * Mail Box
+	 *         GCSR    CRG
+	 * offset   10     610 - Mailbox0
+	 */
+#define TSI148_GCSR_MBOX0	0x610
+#define TSI148_GCSR_MBOX1	0x614
+#define TSI148_GCSR_MBOX2	0x618
+#define TSI148_GCSR_MBOX3	0x61C
+
+static const int TSI148_GCSR_MBOX[4] = { TSI148_GCSR_MBOX0,
+					TSI148_GCSR_MBOX1,
+					TSI148_GCSR_MBOX2,
+					TSI148_GCSR_MBOX3 };
+
+	/*
+	 * CR/CSR
+	 */
+
+	/*
+	 *        CR/CSR   CRG
+	 * offset  7FFF4   FF4 - CSRBCR
+	 * offset  7FFF8   FF8 - CSRBSR
+	 * offset  7FFFC   FFC - CBAR
+	 */
+#define TSI148_CSRBCR	0xFF4
+#define TSI148_CSRBSR	0xFF8
+#define TSI148_CBAR	0xFFC
+
+
+
+
+	/*
+	 *  TSI148 Register Bit Definitions
+	 */
+
+	/*
+	 *  PFCS Register Set
+	 */
+#define TSI148_PCFS_CMMD_SERR          (1<<8)	/* SERR_L out pin ssys err */
+#define TSI148_PCFS_CMMD_PERR          (1<<6)	/* PERR_L out pin  parity */
+#define TSI148_PCFS_CMMD_MSTR          (1<<2)	/* PCI bus master */
+#define TSI148_PCFS_CMMD_MEMSP         (1<<1)	/* PCI mem space access  */
+#define TSI148_PCFS_CMMD_IOSP          (1<<0)	/* PCI I/O space enable */
+
+#define TSI148_PCFS_STAT_RCPVE         (1<<15)	/* Detected Parity Error */
+#define TSI148_PCFS_STAT_SIGSE         (1<<14)	/* Signalled System Error */
+#define TSI148_PCFS_STAT_RCVMA         (1<<13)	/* Received Master Abort */
+#define TSI148_PCFS_STAT_RCVTA         (1<<12)	/* Received Target Abort */
+#define TSI148_PCFS_STAT_SIGTA         (1<<11)	/* Signalled Target Abort */
+#define TSI148_PCFS_STAT_SELTIM        (3<<9)	/* DELSEL Timing */
+#define TSI148_PCFS_STAT_DPAR          (1<<8)	/* Data Parity Err Reported */
+#define TSI148_PCFS_STAT_FAST          (1<<7)	/* Fast back-to-back Cap */
+#define TSI148_PCFS_STAT_P66M          (1<<5)	/* 66 MHz Capable */
+#define TSI148_PCFS_STAT_CAPL          (1<<4)	/* Capab List - address $34 */
+
+/*
+ *  Revision ID/Class Code Registers   (CRG +$008)
+ */
+#define TSI148_PCFS_CLAS_M             (0xFF<<24)	/* Class ID */
+#define TSI148_PCFS_SUBCLAS_M          (0xFF<<16)	/* Sub-Class ID */
+#define TSI148_PCFS_PROGIF_M           (0xFF<<8)	/* Sub-Class ID */
+#define TSI148_PCFS_REVID_M            (0xFF<<0)	/* Rev ID */
+
+/*
+ * Cache Line Size/ Master Latency Timer/ Header Type Registers (CRG + $00C)
+ */
+#define TSI148_PCFS_HEAD_M             (0xFF<<16)	/* Master Lat Timer */
+#define TSI148_PCFS_MLAT_M             (0xFF<<8)	/* Master Lat Timer */
+#define TSI148_PCFS_CLSZ_M             (0xFF<<0)	/* Cache Line Size */
+
+/*
+ *  Memory Base Address Lower Reg (CRG + $010)
+ */
+#define TSI148_PCFS_MBARL_BASEL_M      (0xFFFFF<<12)	/* Base Addr Lower Mask */
+#define TSI148_PCFS_MBARL_PRE          (1<<3)	/* Prefetch */
+#define TSI148_PCFS_MBARL_MTYPE_M      (3<<1)	/* Memory Type Mask */
+#define TSI148_PCFS_MBARL_IOMEM        (1<<0)	/* I/O Space Indicator */
+
+/*
+ *  Message Signaled Interrupt Capabilities Register (CRG + $040)
+ */
+#define TSI148_PCFS_MSICAP_64BAC       (1<<7)	/* 64-bit Address Capable */
+#define TSI148_PCFS_MSICAP_MME_M       (7<<4)	/* Multiple Msg Enable Mask */
+#define TSI148_PCFS_MSICAP_MMC_M       (7<<1)	/* Multiple Msg Capable Mask */
+#define TSI148_PCFS_MSICAP_MSIEN       (1<<0)	/* Msg signaled INT Enable */
+
+/*
+ *  Message Address Lower Register (CRG +$044)
+ */
+#define TSI148_PCFS_MSIAL_M            (0x3FFFFFFF<<2)	/* Mask */
+
+/*
+ *  Message Data Register (CRG + 4C)
+ */
+#define TSI148_PCFS_MSIMD_M            (0xFFFF<<0)	/* Mask */
+
+/*
+ *  PCI-X Capabilities Register (CRG + $050)
+ */
+#define TSI148_PCFS_PCIXCAP_MOST_M     (7<<4)	/* Max outstanding Split Tran */
+#define TSI148_PCFS_PCIXCAP_MMRBC_M    (3<<2)	/* Max Mem Read byte cnt */
+#define TSI148_PCFS_PCIXCAP_ERO        (1<<1)	/* Enable Relaxed Ordering */
+#define TSI148_PCFS_PCIXCAP_DPERE      (1<<0)	/* Data Parity Recover Enable */
+
+/*
+ *  PCI-X Status Register (CRG +$054)
+ */
+#define TSI148_PCFS_PCIXSTAT_RSCEM     (1<<29)	/* Recieved Split Comp Error */
+#define TSI148_PCFS_PCIXSTAT_DMCRS_M   (7<<26)	/* max Cumulative Read Size */
+#define TSI148_PCFS_PCIXSTAT_DMOST_M   (7<<23)	/* max outstanding Split Trans */
+#define TSI148_PCFS_PCIXSTAT_DMMRC_M   (3<<21)	/* max mem read byte count */
+#define TSI148_PCFS_PCIXSTAT_DC        (1<<20)	/* Device Complexity */
+#define TSI148_PCFS_PCIXSTAT_USC       (1<<19)	/* Unexpected Split comp */
+#define TSI148_PCFS_PCIXSTAT_SCD       (1<<18)	/* Split completion discard */
+#define TSI148_PCFS_PCIXSTAT_133C      (1<<17)	/* 133MHz capable */
+#define TSI148_PCFS_PCIXSTAT_64D       (1<<16)	/* 64 bit device */
+#define TSI148_PCFS_PCIXSTAT_BN_M      (0xFF<<8)	/* Bus number */
+#define TSI148_PCFS_PCIXSTAT_DN_M      (0x1F<<3)	/* Device number */
+#define TSI148_PCFS_PCIXSTAT_FN_M      (7<<0)	/* Function Number */
+
+/*
+ *  LCSR Registers
+ */
+
+/*
+ *  Outbound Translation Starting Address Lower
+ */
+#define TSI148_LCSR_OTSAL_M            (0xFFFF<<16)	/* Mask */
+
+/*
+ *  Outbound Translation Ending Address Lower
+ */
+#define TSI148_LCSR_OTEAL_M            (0xFFFF<<16)	/* Mask */
+
+/*
+ *  Outbound Translation Offset Lower
+ */
+#define TSI148_LCSR_OTOFFL_M           (0xFFFF<<16)	/* Mask */
+
+/*
+ *  Outbound Translation 2eSST Broadcast Select
+ */
+#define TSI148_LCSR_OTBS_M             (0xFFFFF<<0)	/* Mask */
+
+/*
+ *  Outbound Translation Attribute
+ */
+#define TSI148_LCSR_OTAT_EN            (1<<31)	/* Window Enable */
+#define TSI148_LCSR_OTAT_MRPFD         (1<<18)	/* Prefetch Disable */
+
+#define TSI148_LCSR_OTAT_PFS_M         (3<<16)	/* Prefetch Size Mask */
+#define TSI148_LCSR_OTAT_PFS_2         (0<<16)	/* 2 Cache Lines P Size */
+#define TSI148_LCSR_OTAT_PFS_4         (1<<16)	/* 4 Cache Lines P Size */
+#define TSI148_LCSR_OTAT_PFS_8         (2<<16)	/* 8 Cache Lines P Size */
+#define TSI148_LCSR_OTAT_PFS_16        (3<<16)	/* 16 Cache Lines P Size */
+
+#define TSI148_LCSR_OTAT_2eSSTM_M      (7<<11)	/* 2eSST Xfer Rate Mask */
+#define TSI148_LCSR_OTAT_2eSSTM_160    (0<<11)	/* 160MB/s 2eSST Xfer Rate */
+#define TSI148_LCSR_OTAT_2eSSTM_267    (1<<11)	/* 267MB/s 2eSST Xfer Rate */
+#define TSI148_LCSR_OTAT_2eSSTM_320    (2<<11)	/* 320MB/s 2eSST Xfer Rate */
+
+#define TSI148_LCSR_OTAT_TM_M          (7<<8)	/* Xfer Protocol Mask */
+#define TSI148_LCSR_OTAT_TM_SCT        (0<<8)	/* SCT Xfer Protocol */
+#define TSI148_LCSR_OTAT_TM_BLT        (1<<8)	/* BLT Xfer Protocol */
+#define TSI148_LCSR_OTAT_TM_MBLT       (2<<8)	/* MBLT Xfer Protocol */
+#define TSI148_LCSR_OTAT_TM_2eVME      (3<<8)	/* 2eVME Xfer Protocol */
+#define TSI148_LCSR_OTAT_TM_2eSST      (4<<8)	/* 2eSST Xfer Protocol */
+#define TSI148_LCSR_OTAT_TM_2eSSTB     (5<<8)	/* 2eSST Bcast Xfer Protocol */
+
+#define TSI148_LCSR_OTAT_DBW_M         (3<<6)	/* Max Data Width */
+#define TSI148_LCSR_OTAT_DBW_16        (0<<6)	/* 16-bit Data Width */
+#define TSI148_LCSR_OTAT_DBW_32        (1<<6)	/* 32-bit Data Width */
+
+#define TSI148_LCSR_OTAT_SUP           (1<<5)	/* Supervisory Access */
+#define TSI148_LCSR_OTAT_PGM           (1<<4)	/* Program Access */
+
+#define TSI148_LCSR_OTAT_AMODE_M       (0xf<<0)	/* Address Mode Mask */
+#define TSI148_LCSR_OTAT_AMODE_A16     (0<<0)	/* A16 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_A24     (1<<0)	/* A24 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_A32     (2<<0)	/* A32 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_A64     (4<<0)	/* A32 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_CRCSR   (5<<0)	/* CR/CSR Address Space */
+#define TSI148_LCSR_OTAT_AMODE_USER1   (8<<0)	/* User1 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_USER2   (9<<0)	/* User2 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_USER3   (10<<0)	/* User3 Address Space */
+#define TSI148_LCSR_OTAT_AMODE_USER4   (11<<0)	/* User4 Address Space */
+
+/*
+ *  VME Master Control Register  CRG+$234
+ */
+#define TSI148_LCSR_VMCTRL_VSA         (1<<27)	/* VMEbus Stop Ack */
+#define TSI148_LCSR_VMCTRL_VS          (1<<26)	/* VMEbus Stop */
+#define TSI148_LCSR_VMCTRL_DHB         (1<<25)	/* Device Has Bus */
+#define TSI148_LCSR_VMCTRL_DWB         (1<<24)	/* Device Wants Bus */
+
+#define TSI148_LCSR_VMCTRL_RMWEN       (1<<20)	/* RMW Enable */
+
+#define TSI148_LCSR_VMCTRL_ATO_M       (7<<16)	/* Master Access Time-out Mask */
+#define TSI148_LCSR_VMCTRL_ATO_32      (0<<16)	/* 32 us */
+#define TSI148_LCSR_VMCTRL_ATO_128     (1<<16)	/* 128 us */
+#define TSI148_LCSR_VMCTRL_ATO_512     (2<<16)	/* 512 us */
+#define TSI148_LCSR_VMCTRL_ATO_2M      (3<<16)	/* 2 ms */
+#define TSI148_LCSR_VMCTRL_ATO_8M      (4<<16)	/* 8 ms */
+#define TSI148_LCSR_VMCTRL_ATO_32M     (5<<16)	/* 32 ms */
+#define TSI148_LCSR_VMCTRL_ATO_128M    (6<<16)	/* 128 ms */
+#define TSI148_LCSR_VMCTRL_ATO_DIS     (7<<16)	/* Disabled */
+
+#define TSI148_LCSR_VMCTRL_VTOFF_M     (7<<12)	/* VMEbus Master Time off */
+#define TSI148_LCSR_VMCTRL_VTOFF_0     (0<<12)	/* 0us */
+#define TSI148_LCSR_VMCTRL_VTOFF_1     (1<<12)	/* 1us */
+#define TSI148_LCSR_VMCTRL_VTOFF_2     (2<<12)	/* 2us */
+#define TSI148_LCSR_VMCTRL_VTOFF_4     (3<<12)	/* 4us */
+#define TSI148_LCSR_VMCTRL_VTOFF_8     (4<<12)	/* 8us */
+#define TSI148_LCSR_VMCTRL_VTOFF_16    (5<<12)	/* 16us */
+#define TSI148_LCSR_VMCTRL_VTOFF_32    (6<<12)	/* 32us */
+#define TSI148_LCSR_VMCTRL_VTOFF_64    (7<<12)	/* 64us */
+
+#define TSI148_LCSR_VMCTRL_VTON_M      (7<<8)	/* VMEbus Master Time On */
+#define TSI148_LCSR_VMCTRL_VTON_4      (0<<8)	/* 8us */
+#define TSI148_LCSR_VMCTRL_VTON_8      (1<<8)	/* 8us */
+#define TSI148_LCSR_VMCTRL_VTON_16     (2<<8)	/* 16us */
+#define TSI148_LCSR_VMCTRL_VTON_32     (3<<8)	/* 32us */
+#define TSI148_LCSR_VMCTRL_VTON_64     (4<<8)	/* 64us */
+#define TSI148_LCSR_VMCTRL_VTON_128    (5<<8)	/* 128us */
+#define TSI148_LCSR_VMCTRL_VTON_256    (6<<8)	/* 256us */
+#define TSI148_LCSR_VMCTRL_VTON_512    (7<<8)	/* 512us */
+
+#define TSI148_LCSR_VMCTRL_VREL_M      (3<<3)	/* VMEbus Master Rel Mode Mask */
+#define TSI148_LCSR_VMCTRL_VREL_T_D    (0<<3)	/* Time on or Done */
+#define TSI148_LCSR_VMCTRL_VREL_T_R_D  (1<<3)	/* Time on and REQ or Done */
+#define TSI148_LCSR_VMCTRL_VREL_T_B_D  (2<<3)	/* Time on and BCLR or Done */
+#define TSI148_LCSR_VMCTRL_VREL_T_D_R  (3<<3)	/* Time on or Done and REQ */
+
+#define TSI148_LCSR_VMCTRL_VFAIR       (1<<2)	/* VMEbus Master Fair Mode */
+#define TSI148_LCSR_VMCTRL_VREQL_M     (3<<0)	/* VMEbus Master Req Level Mask */
+
+/*
+ *  VMEbus Control Register CRG+$238
+ */
+#define TSI148_LCSR_VCTRL_LRE          (1<<31)	/* Late Retry Enable */
+
+#define TSI148_LCSR_VCTRL_DLT_M        (0xF<<24)	/* Deadlock Timer */
+#define TSI148_LCSR_VCTRL_DLT_OFF      (0<<24)	/* Deadlock Timer Off */
+#define TSI148_LCSR_VCTRL_DLT_16       (1<<24)	/* 16 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_32       (2<<24)	/* 32 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_64       (3<<24)	/* 64 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_128      (4<<24)	/* 128 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_256      (5<<24)	/* 256 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_512      (6<<24)	/* 512 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_1024     (7<<24)	/* 1024 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_2048     (8<<24)	/* 2048 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_4096     (9<<24)	/* 4096 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_8192     (0xA<<24)	/* 8192 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_16384    (0xB<<24)	/* 16384 VCLKS */
+#define TSI148_LCSR_VCTRL_DLT_32768    (0xC<<24)	/* 32768 VCLKS */
+
+#define TSI148_LCSR_VCTRL_NERBB        (1<<20)	/* No Early Release of Bus Busy */
+
+#define TSI148_LCSR_VCTRL_SRESET       (1<<17)	/* System Reset */
+#define TSI148_LCSR_VCTRL_LRESET       (1<<16)	/* Local Reset */
+
+#define TSI148_LCSR_VCTRL_SFAILAI      (1<<15)	/* SYSFAIL Auto Slot ID */
+#define TSI148_LCSR_VCTRL_BID_M        (0x1F<<8)	/* Broadcast ID Mask */
+
+#define TSI148_LCSR_VCTRL_ATOEN        (1<<7)	/* Arbiter Time-out Enable */
+#define TSI148_LCSR_VCTRL_ROBIN        (1<<6)	/* VMEbus Round Robin */
+
+#define TSI148_LCSR_VCTRL_GTO_M        (7<<0)	/* VMEbus Global Time-out Mask */
+#define TSI148_LCSR_VCTRL_GTO_8	      (0<<0)	/* 8 us */
+#define TSI148_LCSR_VCTRL_GTO_16	      (1<<0)	/* 16 us */
+#define TSI148_LCSR_VCTRL_GTO_32	      (2<<0)	/* 32 us */
+#define TSI148_LCSR_VCTRL_GTO_64	      (3<<0)	/* 64 us */
+#define TSI148_LCSR_VCTRL_GTO_128      (4<<0)	/* 128 us */
+#define TSI148_LCSR_VCTRL_GTO_256      (5<<0)	/* 256 us */
+#define TSI148_LCSR_VCTRL_GTO_512      (6<<0)	/* 512 us */
+#define TSI148_LCSR_VCTRL_GTO_DIS      (7<<0)	/* Disabled */
+
+/*
+ *  VMEbus Status Register  CRG + $23C
+ */
+#define TSI148_LCSR_VSTAT_CPURST       (1<<15)	/* Clear power up reset */
+#define TSI148_LCSR_VSTAT_BRDFL        (1<<14)	/* Board fail */
+#define TSI148_LCSR_VSTAT_PURSTS       (1<<12)	/* Power up reset status */
+#define TSI148_LCSR_VSTAT_BDFAILS      (1<<11)	/* Board Fail Status */
+#define TSI148_LCSR_VSTAT_SYSFAILS     (1<<10)	/* System Fail Status */
+#define TSI148_LCSR_VSTAT_ACFAILS      (1<<9)	/* AC fail status */
+#define TSI148_LCSR_VSTAT_SCONS        (1<<8)	/* System Cont Status */
+#define TSI148_LCSR_VSTAT_GAP          (1<<5)	/* Geographic Addr Parity */
+#define TSI148_LCSR_VSTAT_GA_M         (0x1F<<0)	/* Geographic Addr Mask */
+
+/*
+ *  PCI Configuration Status Register CRG+$240
+ */
+#define TSI148_LCSR_PSTAT_REQ64S       (1<<6)	/* Request 64 status set */
+#define TSI148_LCSR_PSTAT_M66ENS       (1<<5)	/* M66ENS 66Mhz enable */
+#define TSI148_LCSR_PSTAT_FRAMES       (1<<4)	/* Frame Status */
+#define TSI148_LCSR_PSTAT_IRDYS        (1<<3)	/* IRDY status */
+#define TSI148_LCSR_PSTAT_DEVSELS      (1<<2)	/* DEVL status */
+#define TSI148_LCSR_PSTAT_STOPS        (1<<1)	/* STOP status */
+#define TSI148_LCSR_PSTAT_TRDYS        (1<<0)	/* TRDY status */
+
+/*
+ *  VMEbus Exception Attributes Register  CRG + $268
+ */
+#define TSI148_LCSR_VEAT_VES           (1<<31)	/* Status */
+#define TSI148_LCSR_VEAT_VEOF          (1<<30)	/* Overflow */
+#define TSI148_LCSR_VEAT_VESCL         (1<<29)	/* Status Clear */
+#define TSI148_LCSR_VEAT_2EOT          (1<<21)	/* 2e Odd Termination */
+#define TSI148_LCSR_VEAT_2EST          (1<<20)	/* 2e Slave terminated */
+#define TSI148_LCSR_VEAT_BERR          (1<<19)	/* Bus Error */
+#define TSI148_LCSR_VEAT_LWORD         (1<<18)	/* LWORD_ signal state */
+#define TSI148_LCSR_VEAT_WRITE         (1<<17)	/* WRITE_ signal state */
+#define TSI148_LCSR_VEAT_IACK          (1<<16)	/* IACK_ signal state */
+#define TSI148_LCSR_VEAT_DS1           (1<<15)	/* DS1_ signal state */
+#define TSI148_LCSR_VEAT_DS0           (1<<14)	/* DS0_ signal state */
+#define TSI148_LCSR_VEAT_AM_M          (0x3F<<8)	/* Address Mode Mask */
+#define TSI148_LCSR_VEAT_XAM_M         (0xFF<<0)	/* Master AMode Mask */
+
+
+/*
+ * VMEbus PCI Error Diagnostics PCI/X Attributes Register  CRG + $280
+ */
+#define TSI148_LCSR_EDPAT_EDPCL        (1<<29)
+
+/*
+ *  Inbound Translation Starting Address Lower
+ */
+#define TSI148_LCSR_ITSAL6432_M        (0xFFFF<<16)	/* Mask */
+#define TSI148_LCSR_ITSAL24_M          (0x00FFF<<12)	/* Mask */
+#define TSI148_LCSR_ITSAL16_M          (0x0000FFF<<4)	/* Mask */
+
+/*
+ *  Inbound Translation Ending Address Lower
+ */
+#define TSI148_LCSR_ITEAL6432_M        (0xFFFF<<16)	/* Mask */
+#define TSI148_LCSR_ITEAL24_M          (0x00FFF<<12)	/* Mask */
+#define TSI148_LCSR_ITEAL16_M          (0x0000FFF<<4)	/* Mask */
+
+/*
+ *  Inbound Translation Offset Lower
+ */
+#define TSI148_LCSR_ITOFFL6432_M       (0xFFFF<<16)	/* Mask */
+#define TSI148_LCSR_ITOFFL24_M         (0xFFFFF<<12)	/* Mask */
+#define TSI148_LCSR_ITOFFL16_M         (0xFFFFFFF<<4)	/* Mask */
+
+/*
+ *  Inbound Translation Attribute
+ */
+#define TSI148_LCSR_ITAT_EN            (1<<31)	/* Window Enable */
+#define TSI148_LCSR_ITAT_TH            (1<<18)	/* Prefetch Threshold */
+
+#define TSI148_LCSR_ITAT_VFS_M         (3<<16)	/* Virtual FIFO Size Mask */
+#define TSI148_LCSR_ITAT_VFS_64        (0<<16)	/* 64 bytes Virtual FIFO Size */
+#define TSI148_LCSR_ITAT_VFS_128       (1<<16)	/* 128 bytes Virtual FIFO Sz */
+#define TSI148_LCSR_ITAT_VFS_256       (2<<16)	/* 256 bytes Virtual FIFO Sz */
+#define TSI148_LCSR_ITAT_VFS_512       (3<<16)	/* 512 bytes Virtual FIFO Sz */
+
+#define TSI148_LCSR_ITAT_2eSSTM_M      (7<<12)	/* 2eSST Xfer Rate Mask */
+#define TSI148_LCSR_ITAT_2eSSTM_160    (0<<12)	/* 160MB/s 2eSST Xfer Rate */
+#define TSI148_LCSR_ITAT_2eSSTM_267    (1<<12)	/* 267MB/s 2eSST Xfer Rate */
+#define TSI148_LCSR_ITAT_2eSSTM_320    (2<<12)	/* 320MB/s 2eSST Xfer Rate */
+
+#define TSI148_LCSR_ITAT_2eSSTB        (1<<11)	/* 2eSST Bcast Xfer Protocol */
+#define TSI148_LCSR_ITAT_2eSST         (1<<10)	/* 2eSST Xfer Protocol */
+#define TSI148_LCSR_ITAT_2eVME         (1<<9)	/* 2eVME Xfer Protocol */
+#define TSI148_LCSR_ITAT_MBLT          (1<<8)	/* MBLT Xfer Protocol */
+#define TSI148_LCSR_ITAT_BLT           (1<<7)	/* BLT Xfer Protocol */
+
+#define TSI148_LCSR_ITAT_AS_M          (7<<4)	/* Address Space Mask */
+#define TSI148_LCSR_ITAT_AS_A16        (0<<4)	/* A16 Address Space */
+#define TSI148_LCSR_ITAT_AS_A24        (1<<4)	/* A24 Address Space */
+#define TSI148_LCSR_ITAT_AS_A32        (2<<4)	/* A32 Address Space */
+#define TSI148_LCSR_ITAT_AS_A64        (4<<4)	/* A64 Address Space */
+
+#define TSI148_LCSR_ITAT_SUPR          (1<<3)	/* Supervisor Access */
+#define TSI148_LCSR_ITAT_NPRIV         (1<<2)	/* Non-Priv (User) Access */
+#define TSI148_LCSR_ITAT_PGM           (1<<1)	/* Program Access */
+#define TSI148_LCSR_ITAT_DATA          (1<<0)	/* Data Access */
+
+/*
+ *  GCSR Base Address Lower Address  CRG +$404
+ */
+#define TSI148_LCSR_GBAL_M             (0x7FFFFFF<<5)	/* Mask */
+
+/*
+ *  GCSR Attribute Register CRG + $408
+ */
+#define TSI148_LCSR_GCSRAT_EN          (1<<7)	/* Enable access to GCSR */
+
+#define TSI148_LCSR_GCSRAT_AS_M        (7<<4)	/* Address Space Mask */
+#define TSI148_LCSR_GCSRAT_AS_A16       (0<<4)	/* Address Space 16 */
+#define TSI148_LCSR_GCSRAT_AS_A24       (1<<4)	/* Address Space 24 */
+#define TSI148_LCSR_GCSRAT_AS_A32       (2<<4)	/* Address Space 32 */
+#define TSI148_LCSR_GCSRAT_AS_A64       (4<<4)	/* Address Space 64 */
+
+#define TSI148_LCSR_GCSRAT_SUPR        (1<<3)	/* Sup set -GCSR decoder */
+#define TSI148_LCSR_GCSRAT_NPRIV       (1<<2)	/* Non-Privliged set - CGSR */
+#define TSI148_LCSR_GCSRAT_PGM         (1<<1)	/* Program set - GCSR decoder */
+#define TSI148_LCSR_GCSRAT_DATA        (1<<0)	/* DATA set GCSR decoder */
+
+/*
+ *  CRG Base Address Lower Address  CRG + $410
+ */
+#define TSI148_LCSR_CBAL_M             (0xFFFFF<<12)
+
+/*
+ *  CRG Attribute Register  CRG + $414
+ */
+#define TSI148_LCSR_CRGAT_EN           (1<<7)	/* Enable PRG Access */
+
+#define TSI148_LCSR_CRGAT_AS_M         (7<<4)	/* Address Space */
+#define TSI148_LCSR_CRGAT_AS_A16       (0<<4)	/* Address Space 16 */
+#define TSI148_LCSR_CRGAT_AS_A24       (1<<4)	/* Address Space 24 */
+#define TSI148_LCSR_CRGAT_AS_A32       (2<<4)	/* Address Space 32 */
+#define TSI148_LCSR_CRGAT_AS_A64       (4<<4)	/* Address Space 64 */
+
+#define TSI148_LCSR_CRGAT_SUPR         (1<<3)	/* Supervisor Access */
+#define TSI148_LCSR_CRGAT_NPRIV        (1<<2)	/* Non-Privliged(User) Access */
+#define TSI148_LCSR_CRGAT_PGM          (1<<1)	/* Program Access */
+#define TSI148_LCSR_CRGAT_DATA         (1<<0)	/* Data Access */
+
+/*
+ *  CR/CSR Offset Lower Register  CRG + $41C
+ */
+#define TSI148_LCSR_CROL_M             (0x1FFF<<19)	/* Mask */
+
+/*
+ *  CR/CSR Attribute register  CRG + $420
+ */
+#define TSI148_LCSR_CRAT_EN            (1<<7)	/* Enable access to CR/CSR */
+
+/*
+ *  Location Monitor base address lower register  CRG + $428
+ */
+#define TSI148_LCSR_LMBAL_M            (0x7FFFFFF<<5)	/* Mask */
+
+/*
+ *  Location Monitor Attribute Register  CRG + $42C
+ */
+#define TSI148_LCSR_LMAT_EN            (1<<7)	/* Enable Location Monitor */
+
+#define TSI148_LCSR_LMAT_AS_M          (7<<4)	/* Address Space MASK  */
+#define TSI148_LCSR_LMAT_AS_A16        (0<<4)	/* A16 */
+#define TSI148_LCSR_LMAT_AS_A24        (1<<4)	/* A24 */
+#define TSI148_LCSR_LMAT_AS_A32        (2<<4)	/* A32 */
+#define TSI148_LCSR_LMAT_AS_A64        (4<<4)	/* A64 */
+
+#define TSI148_LCSR_LMAT_SUPR          (1<<3)	/* Supervisor Access */
+#define TSI148_LCSR_LMAT_NPRIV         (1<<2)	/* Non-Priv (User) Access */
+#define TSI148_LCSR_LMAT_PGM           (1<<1)	/* Program Access */
+#define TSI148_LCSR_LMAT_DATA          (1<<0)	/* Data Access  */
+
+/*
+ *  Broadcast Pulse Generator Timer Register  CRG + $438
+ */
+#define TSI148_LCSR_BPGTR_BPGT_M       (0xFFFF<<0)	/* Mask */
+
+/*
+ *  Broadcast Programmable Clock Timer Register  CRG + $43C
+ */
+#define TSI148_LCSR_BPCTR_BPCT_M       (0xFFFFFF<<0)	/* Mask */
+
+/*
+ *  VMEbus Interrupt Control Register           CRG + $43C
+ */
+#define TSI148_LCSR_VICR_CNTS_M        (3<<22)	/* Cntr Source MASK */
+#define TSI148_LCSR_VICR_CNTS_DIS      (1<<22)	/* Cntr Disable */
+#define TSI148_LCSR_VICR_CNTS_IRQ1     (2<<22)	/* IRQ1 to Cntr */
+#define TSI148_LCSR_VICR_CNTS_IRQ2     (3<<22)	/* IRQ2 to Cntr */
+
+#define TSI148_LCSR_VICR_EDGIS_M       (3<<20)	/* Edge interupt MASK */
+#define TSI148_LCSR_VICR_EDGIS_DIS     (1<<20)	/* Edge interupt Disable */
+#define TSI148_LCSR_VICR_EDGIS_IRQ1    (2<<20)	/* IRQ1 to Edge */
+#define TSI148_LCSR_VICR_EDGIS_IRQ2    (3<<20)	/* IRQ2 to Edge */
+
+#define TSI148_LCSR_VICR_IRQIF_M       (3<<18)	/* IRQ1* Function MASK */
+#define TSI148_LCSR_VICR_IRQIF_NORM    (1<<18)	/* Normal */
+#define TSI148_LCSR_VICR_IRQIF_PULSE   (2<<18)	/* Pulse Generator */
+#define TSI148_LCSR_VICR_IRQIF_PROG    (3<<18)	/* Programmable Clock */
+#define TSI148_LCSR_VICR_IRQIF_1U      (4<<18)	/* 1us Clock */
+
+#define TSI148_LCSR_VICR_IRQ2F_M       (3<<16)	/* IRQ2* Function MASK */
+#define TSI148_LCSR_VICR_IRQ2F_NORM    (1<<16)	/* Normal */
+#define TSI148_LCSR_VICR_IRQ2F_PULSE   (2<<16)	/* Pulse Generator */
+#define TSI148_LCSR_VICR_IRQ2F_PROG    (3<<16)	/* Programmable Clock */
+#define TSI148_LCSR_VICR_IRQ2F_1U      (4<<16)	/* 1us Clock */
+
+#define TSI148_LCSR_VICR_BIP           (1<<15)	/* Broadcast Interrupt Pulse */
+
+#define TSI148_LCSR_VICR_IRQC          (1<<12)	/* VMEbus IRQ Clear */
+#define TSI148_LCSR_VICR_IRQS          (1<<11)	/* VMEbus IRQ Status */
+
+#define TSI148_LCSR_VICR_IRQL_M        (7<<8)	/* VMEbus SW IRQ Level Mask */
+#define TSI148_LCSR_VICR_IRQL_1        (1<<8)	/* VMEbus SW IRQ Level 1 */
+#define TSI148_LCSR_VICR_IRQL_2        (2<<8)	/* VMEbus SW IRQ Level 2 */
+#define TSI148_LCSR_VICR_IRQL_3        (3<<8)	/* VMEbus SW IRQ Level 3 */
+#define TSI148_LCSR_VICR_IRQL_4        (4<<8)	/* VMEbus SW IRQ Level 4 */
+#define TSI148_LCSR_VICR_IRQL_5        (5<<8)	/* VMEbus SW IRQ Level 5 */
+#define TSI148_LCSR_VICR_IRQL_6        (6<<8)	/* VMEbus SW IRQ Level 6 */
+#define TSI148_LCSR_VICR_IRQL_7        (7<<8)	/* VMEbus SW IRQ Level 7 */
+
+static const int TSI148_LCSR_VICR_IRQL[8] = { 0, TSI148_LCSR_VICR_IRQL_1,
+			TSI148_LCSR_VICR_IRQL_2, TSI148_LCSR_VICR_IRQL_3,
+			TSI148_LCSR_VICR_IRQL_4, TSI148_LCSR_VICR_IRQL_5,
+			TSI148_LCSR_VICR_IRQL_6, TSI148_LCSR_VICR_IRQL_7 };
+
+#define TSI148_LCSR_VICR_STID_M        (0xFF<<0)	/* Status/ID Mask */
+
+/*
+ *  Interrupt Enable Register   CRG + $440
+ */
+#define TSI148_LCSR_INTEN_DMA1EN       (1<<25)	/* DMAC 1 */
+#define TSI148_LCSR_INTEN_DMA0EN       (1<<24)	/* DMAC 0 */
+#define TSI148_LCSR_INTEN_LM3EN        (1<<23)	/* Location Monitor 3 */
+#define TSI148_LCSR_INTEN_LM2EN        (1<<22)	/* Location Monitor 2 */
+#define TSI148_LCSR_INTEN_LM1EN        (1<<21)	/* Location Monitor 1 */
+#define TSI148_LCSR_INTEN_LM0EN        (1<<20)	/* Location Monitor 0 */
+#define TSI148_LCSR_INTEN_MB3EN        (1<<19)	/* Mail Box 3 */
+#define TSI148_LCSR_INTEN_MB2EN        (1<<18)	/* Mail Box 2 */
+#define TSI148_LCSR_INTEN_MB1EN        (1<<17)	/* Mail Box 1 */
+#define TSI148_LCSR_INTEN_MB0EN        (1<<16)	/* Mail Box 0 */
+#define TSI148_LCSR_INTEN_PERREN       (1<<13)	/* PCI/X Error */
+#define TSI148_LCSR_INTEN_VERREN       (1<<12)	/* VMEbus Error */
+#define TSI148_LCSR_INTEN_VIEEN        (1<<11)	/* VMEbus IRQ Edge */
+#define TSI148_LCSR_INTEN_IACKEN       (1<<10)	/* IACK */
+#define TSI148_LCSR_INTEN_SYSFLEN      (1<<9)	/* System Fail */
+#define TSI148_LCSR_INTEN_ACFLEN       (1<<8)	/* AC Fail */
+#define TSI148_LCSR_INTEN_IRQ7EN       (1<<7)	/* IRQ7 */
+#define TSI148_LCSR_INTEN_IRQ6EN       (1<<6)	/* IRQ6 */
+#define TSI148_LCSR_INTEN_IRQ5EN       (1<<5)	/* IRQ5 */
+#define TSI148_LCSR_INTEN_IRQ4EN       (1<<4)	/* IRQ4 */
+#define TSI148_LCSR_INTEN_IRQ3EN       (1<<3)	/* IRQ3 */
+#define TSI148_LCSR_INTEN_IRQ2EN       (1<<2)	/* IRQ2 */
+#define TSI148_LCSR_INTEN_IRQ1EN       (1<<1)	/* IRQ1 */
+
+static const int TSI148_LCSR_INTEN_LMEN[4] = { TSI148_LCSR_INTEN_LM0EN,
+					TSI148_LCSR_INTEN_LM1EN,
+					TSI148_LCSR_INTEN_LM2EN,
+					TSI148_LCSR_INTEN_LM3EN };
+
+static const int TSI148_LCSR_INTEN_IRQEN[7] = { TSI148_LCSR_INTEN_IRQ1EN,
+					TSI148_LCSR_INTEN_IRQ2EN,
+					TSI148_LCSR_INTEN_IRQ3EN,
+					TSI148_LCSR_INTEN_IRQ4EN,
+					TSI148_LCSR_INTEN_IRQ5EN,
+					TSI148_LCSR_INTEN_IRQ6EN,
+					TSI148_LCSR_INTEN_IRQ7EN };
+
+/*
+ *  Interrupt Enable Out Register CRG + $444
+ */
+#define TSI148_LCSR_INTEO_DMA1EO       (1<<25)	/* DMAC 1 */
+#define TSI148_LCSR_INTEO_DMA0EO       (1<<24)	/* DMAC 0 */
+#define TSI148_LCSR_INTEO_LM3EO        (1<<23)	/* Loc Monitor 3 */
+#define TSI148_LCSR_INTEO_LM2EO        (1<<22)	/* Loc Monitor 2 */
+#define TSI148_LCSR_INTEO_LM1EO        (1<<21)	/* Loc Monitor 1 */
+#define TSI148_LCSR_INTEO_LM0EO        (1<<20)	/* Location Monitor 0 */
+#define TSI148_LCSR_INTEO_MB3EO        (1<<19)	/* Mail Box 3 */
+#define TSI148_LCSR_INTEO_MB2EO        (1<<18)	/* Mail Box 2 */
+#define TSI148_LCSR_INTEO_MB1EO        (1<<17)	/* Mail Box 1 */
+#define TSI148_LCSR_INTEO_MB0EO        (1<<16)	/* Mail Box 0 */
+#define TSI148_LCSR_INTEO_PERREO       (1<<13)	/* PCI/X Error */
+#define TSI148_LCSR_INTEO_VERREO       (1<<12)	/* VMEbus Error */
+#define TSI148_LCSR_INTEO_VIEEO        (1<<11)	/* VMEbus IRQ Edge */
+#define TSI148_LCSR_INTEO_IACKEO       (1<<10)	/* IACK */
+#define TSI148_LCSR_INTEO_SYSFLEO      (1<<9)	/* System Fail */
+#define TSI148_LCSR_INTEO_ACFLEO       (1<<8)	/* AC Fail */
+#define TSI148_LCSR_INTEO_IRQ7EO       (1<<7)	/* IRQ7 */
+#define TSI148_LCSR_INTEO_IRQ6EO       (1<<6)	/* IRQ6 */
+#define TSI148_LCSR_INTEO_IRQ5EO       (1<<5)	/* IRQ5 */
+#define TSI148_LCSR_INTEO_IRQ4EO       (1<<4)	/* IRQ4 */
+#define TSI148_LCSR_INTEO_IRQ3EO       (1<<3)	/* IRQ3 */
+#define TSI148_LCSR_INTEO_IRQ2EO       (1<<2)	/* IRQ2 */
+#define TSI148_LCSR_INTEO_IRQ1EO       (1<<1)	/* IRQ1 */
+
+static const int TSI148_LCSR_INTEO_LMEO[4] = { TSI148_LCSR_INTEO_LM0EO,
+					TSI148_LCSR_INTEO_LM1EO,
+					TSI148_LCSR_INTEO_LM2EO,
+					TSI148_LCSR_INTEO_LM3EO };
+
+static const int TSI148_LCSR_INTEO_IRQEO[7] = { TSI148_LCSR_INTEO_IRQ1EO,
+					TSI148_LCSR_INTEO_IRQ2EO,
+					TSI148_LCSR_INTEO_IRQ3EO,
+					TSI148_LCSR_INTEO_IRQ4EO,
+					TSI148_LCSR_INTEO_IRQ5EO,
+					TSI148_LCSR_INTEO_IRQ6EO,
+					TSI148_LCSR_INTEO_IRQ7EO };
+
+/*
+ *  Interrupt Status Register CRG + $448
+ */
+#define TSI148_LCSR_INTS_DMA1S         (1<<25)	/* DMA 1 */
+#define TSI148_LCSR_INTS_DMA0S         (1<<24)	/* DMA 0 */
+#define TSI148_LCSR_INTS_LM3S          (1<<23)	/* Location Monitor 3 */
+#define TSI148_LCSR_INTS_LM2S          (1<<22)	/* Location Monitor 2 */
+#define TSI148_LCSR_INTS_LM1S          (1<<21)	/* Location Monitor 1 */
+#define TSI148_LCSR_INTS_LM0S          (1<<20)	/* Location Monitor 0 */
+#define TSI148_LCSR_INTS_MB3S          (1<<19)	/* Mail Box 3 */
+#define TSI148_LCSR_INTS_MB2S          (1<<18)	/* Mail Box 2 */
+#define TSI148_LCSR_INTS_MB1S          (1<<17)	/* Mail Box 1 */
+#define TSI148_LCSR_INTS_MB0S          (1<<16)	/* Mail Box 0 */
+#define TSI148_LCSR_INTS_PERRS         (1<<13)	/* PCI/X Error */
+#define TSI148_LCSR_INTS_VERRS         (1<<12)	/* VMEbus Error */
+#define TSI148_LCSR_INTS_VIES          (1<<11)	/* VMEbus IRQ Edge */
+#define TSI148_LCSR_INTS_IACKS         (1<<10)	/* IACK */
+#define TSI148_LCSR_INTS_SYSFLS        (1<<9)	/* System Fail */
+#define TSI148_LCSR_INTS_ACFLS         (1<<8)	/* AC Fail */
+#define TSI148_LCSR_INTS_IRQ7S         (1<<7)	/* IRQ7 */
+#define TSI148_LCSR_INTS_IRQ6S         (1<<6)	/* IRQ6 */
+#define TSI148_LCSR_INTS_IRQ5S         (1<<5)	/* IRQ5 */
+#define TSI148_LCSR_INTS_IRQ4S         (1<<4)	/* IRQ4 */
+#define TSI148_LCSR_INTS_IRQ3S         (1<<3)	/* IRQ3 */
+#define TSI148_LCSR_INTS_IRQ2S         (1<<2)	/* IRQ2 */
+#define TSI148_LCSR_INTS_IRQ1S         (1<<1)	/* IRQ1 */
+
+static const int TSI148_LCSR_INTS_LMS[4] = { TSI148_LCSR_INTS_LM0S,
+					TSI148_LCSR_INTS_LM1S,
+					TSI148_LCSR_INTS_LM2S,
+					TSI148_LCSR_INTS_LM3S };
+
+static const int TSI148_LCSR_INTS_MBS[4] = { TSI148_LCSR_INTS_MB0S,
+					TSI148_LCSR_INTS_MB1S,
+					TSI148_LCSR_INTS_MB2S,
+					TSI148_LCSR_INTS_MB3S };
+
+/*
+ *  Interrupt Clear Register CRG + $44C
+ */
+#define TSI148_LCSR_INTC_DMA1C         (1<<25)	/* DMA 1 */
+#define TSI148_LCSR_INTC_DMA0C         (1<<24)	/* DMA 0 */
+#define TSI148_LCSR_INTC_LM3C          (1<<23)	/* Location Monitor 3 */
+#define TSI148_LCSR_INTC_LM2C          (1<<22)	/* Location Monitor 2 */
+#define TSI148_LCSR_INTC_LM1C          (1<<21)	/* Location Monitor 1 */
+#define TSI148_LCSR_INTC_LM0C          (1<<20)	/* Location Monitor 0 */
+#define TSI148_LCSR_INTC_MB3C          (1<<19)	/* Mail Box 3 */
+#define TSI148_LCSR_INTC_MB2C          (1<<18)	/* Mail Box 2 */
+#define TSI148_LCSR_INTC_MB1C          (1<<17)	/* Mail Box 1 */
+#define TSI148_LCSR_INTC_MB0C          (1<<16)	/* Mail Box 0 */
+#define TSI148_LCSR_INTC_PERRC         (1<<13)	/* VMEbus Error */
+#define TSI148_LCSR_INTC_VERRC         (1<<12)	/* VMEbus Access Time-out */
+#define TSI148_LCSR_INTC_VIEC          (1<<11)	/* VMEbus IRQ Edge */
+#define TSI148_LCSR_INTC_IACKC         (1<<10)	/* IACK */
+#define TSI148_LCSR_INTC_SYSFLC        (1<<9)	/* System Fail */
+#define TSI148_LCSR_INTC_ACFLC         (1<<8)	/* AC Fail */
+
+static const int TSI148_LCSR_INTC_LMC[4] = { TSI148_LCSR_INTC_LM0C,
+					TSI148_LCSR_INTC_LM1C,
+					TSI148_LCSR_INTC_LM2C,
+					TSI148_LCSR_INTC_LM3C };
+
+static const int TSI148_LCSR_INTC_MBC[4] = { TSI148_LCSR_INTC_MB0C,
+					TSI148_LCSR_INTC_MB1C,
+					TSI148_LCSR_INTC_MB2C,
+					TSI148_LCSR_INTC_MB3C };
+
+/*
+ *  Interrupt Map Register 1 CRG + $458
+ */
+#define TSI148_LCSR_INTM1_DMA1M_M      (3<<18)	/* DMA 1 */
+#define TSI148_LCSR_INTM1_DMA0M_M      (3<<16)	/* DMA 0 */
+#define TSI148_LCSR_INTM1_LM3M_M       (3<<14)	/* Location Monitor 3 */
+#define TSI148_LCSR_INTM1_LM2M_M       (3<<12)	/* Location Monitor 2 */
+#define TSI148_LCSR_INTM1_LM1M_M       (3<<10)	/* Location Monitor 1 */
+#define TSI148_LCSR_INTM1_LM0M_M       (3<<8)	/* Location Monitor 0 */
+#define TSI148_LCSR_INTM1_MB3M_M       (3<<6)	/* Mail Box 3 */
+#define TSI148_LCSR_INTM1_MB2M_M       (3<<4)	/* Mail Box 2 */
+#define TSI148_LCSR_INTM1_MB1M_M       (3<<2)	/* Mail Box 1 */
+#define TSI148_LCSR_INTM1_MB0M_M       (3<<0)	/* Mail Box 0 */
+
+/*
+ *  Interrupt Map Register 2 CRG + $45C
+ */
+#define TSI148_LCSR_INTM2_PERRM_M      (3<<26)	/* PCI Bus Error */
+#define TSI148_LCSR_INTM2_VERRM_M      (3<<24)	/* VMEbus Error */
+#define TSI148_LCSR_INTM2_VIEM_M       (3<<22)	/* VMEbus IRQ Edge */
+#define TSI148_LCSR_INTM2_IACKM_M      (3<<20)	/* IACK */
+#define TSI148_LCSR_INTM2_SYSFLM_M     (3<<18)	/* System Fail */
+#define TSI148_LCSR_INTM2_ACFLM_M      (3<<16)	/* AC Fail */
+#define TSI148_LCSR_INTM2_IRQ7M_M      (3<<14)	/* IRQ7 */
+#define TSI148_LCSR_INTM2_IRQ6M_M      (3<<12)	/* IRQ6 */
+#define TSI148_LCSR_INTM2_IRQ5M_M      (3<<10)	/* IRQ5 */
+#define TSI148_LCSR_INTM2_IRQ4M_M      (3<<8)	/* IRQ4 */
+#define TSI148_LCSR_INTM2_IRQ3M_M      (3<<6)	/* IRQ3 */
+#define TSI148_LCSR_INTM2_IRQ2M_M      (3<<4)	/* IRQ2 */
+#define TSI148_LCSR_INTM2_IRQ1M_M      (3<<2)	/* IRQ1 */
+
+/*
+ *  DMA Control (0-1) Registers CRG + $500
+ */
+#define TSI148_LCSR_DCTL_ABT           (1<<27)	/* Abort */
+#define TSI148_LCSR_DCTL_PAU           (1<<26)	/* Pause */
+#define TSI148_LCSR_DCTL_DGO           (1<<25)	/* DMA Go */
+
+#define TSI148_LCSR_DCTL_MOD           (1<<23)	/* Mode */
+
+#define TSI148_LCSR_DCTL_VBKS_M        (7<<12)	/* VMEbus block Size MASK */
+#define TSI148_LCSR_DCTL_VBKS_32       (0<<12)	/* VMEbus block Size 32 */
+#define TSI148_LCSR_DCTL_VBKS_64       (1<<12)	/* VMEbus block Size 64 */
+#define TSI148_LCSR_DCTL_VBKS_128      (2<<12)	/* VMEbus block Size 128 */
+#define TSI148_LCSR_DCTL_VBKS_256      (3<<12)	/* VMEbus block Size 256 */
+#define TSI148_LCSR_DCTL_VBKS_512      (4<<12)	/* VMEbus block Size 512 */
+#define TSI148_LCSR_DCTL_VBKS_1024     (5<<12)	/* VMEbus block Size 1024 */
+#define TSI148_LCSR_DCTL_VBKS_2048     (6<<12)	/* VMEbus block Size 2048 */
+#define TSI148_LCSR_DCTL_VBKS_4096     (7<<12)	/* VMEbus block Size 4096 */
+
+#define TSI148_LCSR_DCTL_VBOT_M        (7<<8)	/* VMEbus back-off MASK */
+#define TSI148_LCSR_DCTL_VBOT_0        (0<<8)	/* VMEbus back-off  0us */
+#define TSI148_LCSR_DCTL_VBOT_1        (1<<8)	/* VMEbus back-off 1us */
+#define TSI148_LCSR_DCTL_VBOT_2        (2<<8)	/* VMEbus back-off 2us */
+#define TSI148_LCSR_DCTL_VBOT_4        (3<<8)	/* VMEbus back-off 4us */
+#define TSI148_LCSR_DCTL_VBOT_8        (4<<8)	/* VMEbus back-off 8us */
+#define TSI148_LCSR_DCTL_VBOT_16       (5<<8)	/* VMEbus back-off 16us */
+#define TSI148_LCSR_DCTL_VBOT_32       (6<<8)	/* VMEbus back-off 32us */
+#define TSI148_LCSR_DCTL_VBOT_64       (7<<8)	/* VMEbus back-off 64us */
+
+#define TSI148_LCSR_DCTL_PBKS_M        (7<<4)	/* PCI block size MASK */
+#define TSI148_LCSR_DCTL_PBKS_32       (0<<4)	/* PCI block size 32 bytes */
+#define TSI148_LCSR_DCTL_PBKS_64       (1<<4)	/* PCI block size 64 bytes */
+#define TSI148_LCSR_DCTL_PBKS_128      (2<<4)	/* PCI block size 128 bytes */
+#define TSI148_LCSR_DCTL_PBKS_256      (3<<4)	/* PCI block size 256 bytes */
+#define TSI148_LCSR_DCTL_PBKS_512      (4<<4)	/* PCI block size 512 bytes */
+#define TSI148_LCSR_DCTL_PBKS_1024     (5<<4)	/* PCI block size 1024 bytes */
+#define TSI148_LCSR_DCTL_PBKS_2048     (6<<4)	/* PCI block size 2048 bytes */
+#define TSI148_LCSR_DCTL_PBKS_4096     (7<<4)	/* PCI block size 4096 bytes */
+
+#define TSI148_LCSR_DCTL_PBOT_M        (7<<0)	/* PCI back off MASK */
+#define TSI148_LCSR_DCTL_PBOT_0        (0<<0)	/* PCI back off 0us */
+#define TSI148_LCSR_DCTL_PBOT_1        (1<<0)	/* PCI back off 1us */
+#define TSI148_LCSR_DCTL_PBOT_2        (2<<0)	/* PCI back off 2us */
+#define TSI148_LCSR_DCTL_PBOT_4        (3<<0)	/* PCI back off 3us */
+#define TSI148_LCSR_DCTL_PBOT_8        (4<<0)	/* PCI back off 4us */
+#define TSI148_LCSR_DCTL_PBOT_16       (5<<0)	/* PCI back off 8us */
+#define TSI148_LCSR_DCTL_PBOT_32       (6<<0)	/* PCI back off 16us */
+#define TSI148_LCSR_DCTL_PBOT_64       (7<<0)	/* PCI back off 32us */
+
+/*
+ *  DMA Status Registers (0-1)  CRG + $504
+ */
+#define TSI148_LCSR_DSTA_SMA           (1<<31)	/* PCI Signalled Master Abt */
+#define TSI148_LCSR_DSTA_RTA           (1<<30)	/* PCI Received Target Abt */
+#define TSI148_LCSR_DSTA_MRC           (1<<29)	/* PCI Max Retry Count */
+#define TSI148_LCSR_DSTA_VBE           (1<<28)	/* VMEbus error */
+#define TSI148_LCSR_DSTA_ABT           (1<<27)	/* Abort */
+#define TSI148_LCSR_DSTA_PAU           (1<<26)	/* Pause */
+#define TSI148_LCSR_DSTA_DON           (1<<25)	/* Done */
+#define TSI148_LCSR_DSTA_BSY           (1<<24)	/* Busy */
+
+/*
+ *  DMA Current Link Address Lower (0-1)
+ */
+#define TSI148_LCSR_DCLAL_M            (0x3FFFFFF<<6)	/* Mask */
+
+/*
+ *  DMA Source Attribute (0-1) Reg
+ */
+#define TSI148_LCSR_DSAT_TYP_M         (3<<28)	/* Source Bus Type */
+#define TSI148_LCSR_DSAT_TYP_PCI       (0<<28)	/* PCI Bus */
+#define TSI148_LCSR_DSAT_TYP_VME       (1<<28)	/* VMEbus */
+#define TSI148_LCSR_DSAT_TYP_PAT       (2<<28)	/* Data Pattern */
+
+#define TSI148_LCSR_DSAT_PSZ           (1<<25)	/* Pattern Size */
+#define TSI148_LCSR_DSAT_NIN           (1<<24)	/* No Increment */
+
+#define TSI148_LCSR_DSAT_2eSSTM_M      (3<<11)	/* 2eSST Trans Rate Mask */
+#define TSI148_LCSR_DSAT_2eSSTM_160    (0<<11)	/* 160 MB/s */
+#define TSI148_LCSR_DSAT_2eSSTM_267    (1<<11)	/* 267 MB/s */
+#define TSI148_LCSR_DSAT_2eSSTM_320    (2<<11)	/* 320 MB/s */
+
+#define TSI148_LCSR_DSAT_TM_M          (7<<8)	/* Bus Transfer Protocol Mask */
+#define TSI148_LCSR_DSAT_TM_SCT        (0<<8)	/* SCT */
+#define TSI148_LCSR_DSAT_TM_BLT        (1<<8)	/* BLT */
+#define TSI148_LCSR_DSAT_TM_MBLT       (2<<8)	/* MBLT */
+#define TSI148_LCSR_DSAT_TM_2eVME      (3<<8)	/* 2eVME */
+#define TSI148_LCSR_DSAT_TM_2eSST      (4<<8)	/* 2eSST */
+#define TSI148_LCSR_DSAT_TM_2eSSTB     (5<<8)	/* 2eSST Broadcast */
+
+#define TSI148_LCSR_DSAT_DBW_M         (3<<6)	/* Max Data Width MASK */
+#define TSI148_LCSR_DSAT_DBW_16        (0<<6)	/* 16 Bits */
+#define TSI148_LCSR_DSAT_DBW_32        (1<<6)	/* 32 Bits */
+
+#define TSI148_LCSR_DSAT_SUP           (1<<5)	/* Supervisory Mode */
+#define TSI148_LCSR_DSAT_PGM           (1<<4)	/* Program Mode */
+
+#define TSI148_LCSR_DSAT_AMODE_M       (0xf<<0)	/* Address Space Mask */
+#define TSI148_LCSR_DSAT_AMODE_A16     (0<<0)	/* A16 */
+#define TSI148_LCSR_DSAT_AMODE_A24     (1<<0)	/* A24 */
+#define TSI148_LCSR_DSAT_AMODE_A32     (2<<0)	/* A32 */
+#define TSI148_LCSR_DSAT_AMODE_A64     (4<<0)	/* A64 */
+#define TSI148_LCSR_DSAT_AMODE_CRCSR   (5<<0)	/* CR/CSR */
+#define TSI148_LCSR_DSAT_AMODE_USER1   (8<<0)	/* User1 */
+#define TSI148_LCSR_DSAT_AMODE_USER2   (9<<0)	/* User2 */
+#define TSI148_LCSR_DSAT_AMODE_USER3   (0xa<<0)	/* User3 */
+#define TSI148_LCSR_DSAT_AMODE_USER4   (0xb<<0)	/* User4 */
+
+/*
+ *  DMA Destination Attribute Registers (0-1)
+ */
+#define TSI148_LCSR_DDAT_TYP_PCI       (0<<28)	/* Destination PCI Bus  */
+#define TSI148_LCSR_DDAT_TYP_VME       (1<<28)	/* Destination VMEbus */
+
+#define TSI148_LCSR_DDAT_2eSSTM_M      (3<<11)	/* 2eSST Transfer Rate Mask */
+#define TSI148_LCSR_DDAT_2eSSTM_160    (0<<11)	/* 160 MB/s */
+#define TSI148_LCSR_DDAT_2eSSTM_267    (1<<11)	/* 267 MB/s */
+#define TSI148_LCSR_DDAT_2eSSTM_320    (2<<11)	/* 320 MB/s */
+
+#define TSI148_LCSR_DDAT_TM_M          (7<<8)	/* Bus Transfer Protocol Mask */
+#define TSI148_LCSR_DDAT_TM_SCT        (0<<8)	/* SCT */
+#define TSI148_LCSR_DDAT_TM_BLT        (1<<8)	/* BLT */
+#define TSI148_LCSR_DDAT_TM_MBLT       (2<<8)	/* MBLT */
+#define TSI148_LCSR_DDAT_TM_2eVME      (3<<8)	/* 2eVME */
+#define TSI148_LCSR_DDAT_TM_2eSST      (4<<8)	/* 2eSST */
+#define TSI148_LCSR_DDAT_TM_2eSSTB     (5<<8)	/* 2eSST Broadcast */
+
+#define TSI148_LCSR_DDAT_DBW_M         (3<<6)	/* Max Data Width MASK */
+#define TSI148_LCSR_DDAT_DBW_16        (0<<6)	/* 16 Bits */
+#define TSI148_LCSR_DDAT_DBW_32        (1<<6)	/* 32 Bits */
+
+#define TSI148_LCSR_DDAT_SUP           (1<<5)	/* Supervisory/User Access */
+#define TSI148_LCSR_DDAT_PGM           (1<<4)	/* Program/Data Access */
+
+#define TSI148_LCSR_DDAT_AMODE_M       (0xf<<0)	/* Address Space Mask */
+#define TSI148_LCSR_DDAT_AMODE_A16      (0<<0)	/* A16 */
+#define TSI148_LCSR_DDAT_AMODE_A24      (1<<0)	/* A24 */
+#define TSI148_LCSR_DDAT_AMODE_A32      (2<<0)	/* A32 */
+#define TSI148_LCSR_DDAT_AMODE_A64      (4<<0)	/* A64 */
+#define TSI148_LCSR_DDAT_AMODE_CRCSR   (5<<0)	/* CRC/SR */
+#define TSI148_LCSR_DDAT_AMODE_USER1   (8<<0)	/* User1 */
+#define TSI148_LCSR_DDAT_AMODE_USER2   (9<<0)	/* User2 */
+#define TSI148_LCSR_DDAT_AMODE_USER3   (0xa<<0)	/* User3 */
+#define TSI148_LCSR_DDAT_AMODE_USER4   (0xb<<0)	/* User4 */
+
+/*
+ *  DMA Next Link Address Lower
+ */
+#define TSI148_LCSR_DNLAL_DNLAL_M      (0x3FFFFFF<<6)	/* Address Mask */
+#define TSI148_LCSR_DNLAL_LLA          (1<<0)	/* Last Link Address Indicator */
+
+/*
+ *  DMA 2eSST Broadcast Select
+ */
+#define TSI148_LCSR_DBS_M              (0x1FFFFF<<0)	/* Mask */
+
+/*
+ *  GCSR Register Group
+ */
+
+/*
+ *  GCSR Control and Status Register  CRG + $604
+ */
+#define TSI148_GCSR_GCTRL_LRST         (1<<15)	/* Local Reset */
+#define TSI148_GCSR_GCTRL_SFAILEN      (1<<14)	/* System Fail enable */
+#define TSI148_GCSR_GCTRL_BDFAILS      (1<<13)	/* Board Fail Status */
+#define TSI148_GCSR_GCTRL_SCON         (1<<12)	/* System Copntroller */
+#define TSI148_GCSR_GCTRL_MEN          (1<<11)	/* Module Enable (READY) */
+
+#define TSI148_GCSR_GCTRL_LMI3S        (1<<7)	/* Loc Monitor 3 Int Status */
+#define TSI148_GCSR_GCTRL_LMI2S        (1<<6)	/* Loc Monitor 2 Int Status */
+#define TSI148_GCSR_GCTRL_LMI1S        (1<<5)	/* Loc Monitor 1 Int Status */
+#define TSI148_GCSR_GCTRL_LMI0S        (1<<4)	/* Loc Monitor 0 Int Status */
+#define TSI148_GCSR_GCTRL_MBI3S        (1<<3)	/* Mail box 3 Int Status */
+#define TSI148_GCSR_GCTRL_MBI2S        (1<<2)	/* Mail box 2 Int Status */
+#define TSI148_GCSR_GCTRL_MBI1S        (1<<1)	/* Mail box 1 Int Status */
+#define TSI148_GCSR_GCTRL_MBI0S        (1<<0)	/* Mail box 0 Int Status */
+
+#define TSI148_GCSR_GAP                (1<<5)	/* Geographic Addr Parity */
+#define TSI148_GCSR_GA_M               (0x1F<<0)	/* Geographic Address Mask */
+
+/*
+ *  CR/CSR Register Group
+ */
+
+/*
+ *  CR/CSR Bit Clear Register CRG + $FF4
+ */
+#define TSI148_CRCSR_CSRBCR_LRSTC      (1<<7)	/* Local Reset Clear */
+#define TSI148_CRCSR_CSRBCR_SFAILC     (1<<6)	/* System Fail Enable Clear */
+#define TSI148_CRCSR_CSRBCR_BDFAILS    (1<<5)	/* Board Fail Status */
+#define TSI148_CRCSR_CSRBCR_MENC       (1<<4)	/* Module Enable Clear */
+#define TSI148_CRCSR_CSRBCR_BERRSC     (1<<3)	/* Bus Error Status Clear */
+
+/*
+ *  CR/CSR Bit Set Register CRG+$FF8
+ */
+#define TSI148_CRCSR_CSRBSR_LISTS      (1<<7)	/* Local Reset Clear */
+#define TSI148_CRCSR_CSRBSR_SFAILS     (1<<6)	/* System Fail Enable Clear */
+#define TSI148_CRCSR_CSRBSR_BDFAILS    (1<<5)	/* Board Fail Status */
+#define TSI148_CRCSR_CSRBSR_MENS       (1<<4)	/* Module Enable Clear */
+#define TSI148_CRCSR_CSRBSR_BERRS      (1<<3)	/* Bus Error Status Clear */
+
+/*
+ *  CR/CSR Base Address Register CRG + FFC
+ */
+#define TSI148_CRCSR_CBAR_M            (0x1F<<3)	/* Mask */
+
+#endif				/* TSI148_H */


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

* [patch 5/5] Staging: vme: add TODO file
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
                     ` (3 preceding siblings ...)
  2009-08-03 21:01   ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Greg K-H
@ 2009-08-03 21:01   ` Greg K-H
  2009-08-04  7:56   ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Martyn Welch
  2009-08-08 22:25   ` Emilio G. Cota
  6 siblings, 0 replies; 65+ messages in thread
From: Greg K-H @ 2009-08-03 21:01 UTC (permalink / raw)
  To: linux-kernel, devel; +Cc: Martyn Welch

[-- Attachment #1: staging-vme-add-todo-file.patch --]
[-- Type: text/plain, Size: 13712 bytes --]

From: Martyn Welch <martyn.welch@gefanuc.com>

This describes the current vme api, along with a list of things
that needs to be fixed up.

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/staging/vme/TODO |  388 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 388 insertions(+)

--- /dev/null
+++ b/drivers/staging/vme/TODO
@@ -0,0 +1,388 @@
+			VME Device Driver API
+			=====================
+
+Driver registration
+===================
+
+As with other subsystems within the Linux kernel, VME device drivers register
+with the VME subsystem, typically called from the devices init routine.  This is
+achieved via a call to the follwoing function:
+
+	int vme_register_driver (struct vme_driver *driver);
+
+If driver registration is successful this function returns zero, if an error
+occurred a negative error code will be returned.
+
+A pointer to a structure of type ???vme_driver??? must be provided to the
+registration function. The structure is as follows:
+
+	struct vme_driver {
+		struct list_head node;
+		char *name;
+		const struct vme_device_id *bind_table;
+		int (*probe)  (struct device *, int, int);
+		int (*remove) (struct device *, int, int);
+		void (*shutdown) (void);
+		struct device_driver    driver;
+	};
+
+At the minimum, the ???.name???, ???.probe??? and ???.bind_table??? elements of this
+structure should be correctly set. The ???.name??? element is a pointer to a string
+holding the device driver???s name. The ???.probe??? element should contain a pointer
+to the probe routine.
+
+The arguments of the probe routine are as follows:
+
+	probe(struct device *dev, int bus, int slot);
+
+The ???.bind_table??? is a pointer to an array of type ???vme_device_id???:
+
+	struct vme_device_id {
+		int bus;
+		int slot;
+	};
+
+Each structure in this array should provide a bus and slot number where the core
+should probe, using the driver???s probe routine, for a device on the specified
+VME bus.
+
+The VME subsystem supports a single VME driver per ???slot???. There are considered
+to be 32 slots per bus, one for each slot-ID as defined in the ANSI/VITA 1-1994
+specification and are analogious to the physical slots on the VME backplane.
+
+A function is also provided to unregister the driver from the VME core and is
+usually called from the device driver???s exit routine:
+
+	void vme_unregister_driver (struct vme_driver *driver);
+
+
+Resource management
+===================
+
+Once a driver has registered with the VME core the provided probe routine will
+be called for each of the bus/slot combination that becomes valid as VME buses
+are themselves registered.  The probe routine is passed a pointer to the devices
+device structure. This pointer should be saved, it will be required for
+requesting VME resources.
+
+The driver can request ownership of one or more master windows, slave windows
+and/or dma channels. Rather than allowing the device driver to request a
+specific window or DMA channel (which may be used by a different driver) this
+driver allows a resource to be assigned based on the required attributes of the
+driver in question:
+
+	struct vme_resource * vme_master_request(struct device *dev,
+		vme_address_t aspace, vme_cycle_t cycle, vme_width_t width);
+
+	struct vme_resource * vme_slave_request(struct device *dev,
+		vme_address_t aspace, vme_cycle_t cycle);
+
+TODO:	DMA Resource Allocation incomplete. No attribute based selection.
+
+	struct vme_resource *vme_request_dma(struct device *dev);
+
+For slave windows these attributes are split into those of type ???vme_address_t???
+and ???vme_cycle_t???. Master windows add a further set of attributes ???vme_cycle_t???.
+These attributes are defined as bitmasks and as such any combination of the
+attributes can be requested for a single window, the core will assign a window
+that meets the requirements, returning a pointer of type vme_resource that
+should be used to identify the allocated resource when it is used. If an
+unallocated window fitting the requirements can not be found a NULL pointer will
+be returned.
+
+Functions are also provided to free window allocations once they are no longer
+required. These functions should be passed the pointer to the resource provided
+during resource allocation:
+
+	void vme_master_free(struct vme_resource *res);
+
+	void vme_slave_free(struct vme_resource *res);
+
+	void vme_dma_free(struct vme_resource *res);
+
+
+Master windows
+==============
+
+Master windows provide access from the local processor[s] out onto the VME bus.
+The number of windows available and the available access modes is dependant on
+the underlying chipset. A window must be configured before it can be used.
+
+
+Master window configuration
+---------------------------
+
+Once a master window has been assigned the following functions can be used to
+configure it and retrieve the current settings:
+
+	int vme_master_set (struct vme_resource *res, int enabled,
+		unsigned long long base, unsigned long long size,
+		vme_address_t aspace, vme_cycle_t cycle, vme_width_t width);
+
+	int vme_master_get (struct vme_resource *res, int *enabled,
+		unsigned long long *base, unsigned long long *size,
+		vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *width);
+
+The address spaces, transfer widths and cycle types are the same as described
+under resource management, however some of the options are mutually exclusive.
+For example, only one address space may be specified.
+
+These functions return 0 on success or an error code should the call fail.
+
+
+Master window broadcast select mask
+-----------------------------------
+
+TODO:	Add functions to set and get Broadcast Select mask:
+
+	int vme_master_bmsk_set (struct vme_resource *res, int mask);
+	int vme_master_bmsk_get (struct vme_resource *res, int *mask);
+
+
+Master window access
+--------------------
+
+The following functions can be used to read from and write to configured master
+windows. These functions return the number of bytes copied:
+
+	ssize_t vme_master_read(struct vme_resource *res, void *buf,
+		size_t count, loff_t offset);
+
+	ssize_t vme_master_write(struct vme_resource *res, void *buf,
+		size_t count, loff_t offset);
+
+In addition to simple reads and writes, a function is provided to do a
+read-modify-write transaction. This function returns the original value of the
+VME bus location :
+
+	unsigned int vme_master_rmw (struct vme_resource *res,
+		unsigned int mask, unsigned int compare, unsigned int swap,
+		loff_t offset);
+
+This functions by reading the offset, applying the mask. If the bits selected in
+the mask match with the values of the corresponding bits in the compare field,
+the value of swap is written the specified offset.
+
+
+Slave windows
+=============
+
+Slave windows provide devices on the VME bus access into mapped portions of the
+local memory. The number of windows available and the access modes that can be
+used is dependant on the underlying chipset. A window must be configured before
+it can be used.
+
+
+Slave window configuration
+--------------------------
+
+Once a slave window has been assigned the following functions can be used to
+configure it and retrieve the current settings:
+
+	int vme_slave_set (struct vme_resource *res, int enabled,
+		unsigned long long base, unsigned long long size,
+		dma_addr_t mem, vme_address_t aspace, vme_cycle_t cycle);
+
+	int vme_slave_get (struct vme_resource *res, int *enabled,
+		unsigned long long *base, unsigned long long *size,
+		dma_addr_t *mem, vme_address_t *aspace, vme_cycle_t *cycle);
+
+The address spaces, transfer widths and cycle types are the same as described
+under resource management, however some of the options are mutually exclusive.
+For example, only one address space may be specified.
+
+These functions return 0 on success or an error code should the call fail.
+
+
+Slave window buffer allocation
+------------------------------
+
+Functions are provided to allow the user to allocate and free a contiguous
+buffers which will be accessible by the VME bridge. These functions do not have
+to be used, other methods can be used to allocate a buffer, though care must be
+taken to ensure that they are contiguous and accessible by the VME bridge:
+
+	void * vme_alloc_consistent(struct vme_resource *res, size_t size,
+		dma_addr_t *mem);
+
+	void vme_free_consistent(struct vme_resource *res, size_t size,
+		void *virt,	dma_addr_t mem);
+
+
+Slave window access
+-------------------
+
+Slave windows map local memory onto the VME bus, the standard methods for
+accessing memory should be used.
+
+
+DMA channels
+============
+
+The VME DMA transfer provides the ability to run link-list DMA transfers. The
+API introduces the concept of DMA lists. Each DMA list is a link-list which can
+be passed to a DMA controller. Multiple lists can be created, extended,
+executed, reused and destroyed.
+
+
+List Management
+---------------
+
+The following functions are provided to create and destroy DMA lists. Execution
+of a list will not automatically destroy the list, thus enabling a list to be
+reused for repetitive tasks:
+
+	struct vme_dma_list *vme_new_dma_list(struct vme_resource *res);
+
+	int vme_dma_list_free(struct vme_dma_list *list);
+
+
+List Population
+---------------
+
+An item can be added to a list using the following function ( the source and
+destination attributes need to be created before calling this function, this is
+covered under "Transfer Attributes"):
+
+	int vme_dma_list_add(struct vme_dma_list *list,
+		struct vme_dma_attr *src, struct vme_dma_attr *dest,
+		size_t count);
+
+
+Transfer Attributes
+-------------------
+
+The attributes for the source and destination are handled separately from adding
+an item to a list. This is due to the diverse attributes required for each type
+of source and destination. There are functions to create attributes for PCI, VME
+and pattern sources and destinations (where appropriate):
+
+Pattern source:
+
+	struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern,
+		vme_pattern_t type);
+
+PCI source or destination:
+
+	struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t mem);
+
+VME source or destination:
+
+	struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long base,
+		vme_address_t aspace, vme_cycle_t cycle, vme_width_t width);
+
+The following function should be used to free an attribute:
+
+	void vme_dma_free_attribute(struct vme_dma_attr *attr);
+
+
+List Execution
+--------------
+
+The following function queues a list for execution. The function will return
+once the list has been executed:
+
+	int vme_dma_list_exec(struct vme_dma_list *list);
+
+
+Interrupts
+==========
+
+The VME API provides functions to attach and detach callbacks to specific VME
+level and status ID combinations and for the generation of VME interrupts with
+specific VME level and status IDs.
+
+
+Attaching Interrupt Handlers
+----------------------------
+
+The following functions can be used to attach and free a specific VME level and
+status ID combination. Any given combination can only be assigned a single
+callback function. A void pointer parameter is provided, the value of which is
+passed to the callback function, the use of this pointer is user undefined:
+
+	int vme_request_irq(struct device *dev, int level, int statid,
+		void (*callback)(int, int, void *), void *priv);
+
+	void vme_free_irq(struct device *dev, int level, int statid);
+
+The callback parameters are as follows. Care must be taken in writing a callback
+function, callback functions run in interrupt context:
+
+	void callback(int level, int statid, void *priv);
+
+
+Interrupt Generation
+--------------------
+
+The following function can be used to generate a VME interrupt at a given VME
+level and VME status ID:
+
+	int vme_generate_irq(struct device *dev, int level, int statid);
+
+
+Location monitors
+=================
+
+The VME API provides the following functionality to configure the location
+monitor.
+
+
+Location Monitor Management
+---------------------------
+
+TODO:	Provide a mechanism to request use of the location monitor. The location
+	monitors can be moved and we only want one driver to be able to do that
+	at a time! We also need to be able to free the location monitor for
+	others to use.
+
+	struct vme_resource * vme_request_lm(struct device *dev);
+
+	void vme_free_lm(struct vme_resource * res);
+
+
+Location Monitor Configuration
+------------------------------
+
+TODO:	Change to struct "vme_resource *res" rather than "struct device *dev".
+
+The following functions are provided to configure the location and mode of the
+location monitor:
+
+	int vme_lm_set(struct device *dev, unsigned long long base,
+		vme_address_t aspace, vme_cycle_t cycle);
+
+	int vme_lm_get(struct device *dev, unsigned long long *base,
+		vme_address_t *aspace, vme_cycle_t *cycle);
+
+
+Location Monitor Use
+--------------------
+
+TODO:	Change to struct "vme_resource *res" rather than "struct device *dev".
+
+The following functions allow a callback to be attached and detached from each
+location monitor location. The API currently supports 4 location monitors,
+monitoring 4 adjacent locations:
+
+	int vme_lm_attach(struct device *dev, int num,
+		void (*callback)(int));
+
+	int vme_lm_detach(struct device *dev, int num);
+
+The callback function is declared as follows.
+
+	void callback(int num);
+
+
+CR/CSR
+======
+
+TODO:	The VME API needs functions to access the CR/CSR buffer.
+
+Slot Detection
+==============
+
+This function returns the slot ID of the provided bridge.
+
+	int vme_slot_get(struct device *dev);


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

* Re: [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver
  2009-08-03 21:01   ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Greg K-H
@ 2009-08-03 22:50     ` Jiri Slaby
  2009-08-03 22:55       ` Jiri Slaby
  2009-08-05 16:33       ` [PATCH] Staging: Correct tsi-148 VME interrupt free routine Martyn Welch
  2009-08-09  0:09     ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Emilio G. Cota
  1 sibling, 2 replies; 65+ messages in thread
From: Jiri Slaby @ 2009-08-03 22:50 UTC (permalink / raw)
  To: martyn.welch; +Cc: Greg K-H, linux-kernel, devel

On 08/03/2009 11:01 PM, Greg K-H wrote:
> +void tsi148_free_irq(int level, int statid)
> +{
> +	u32 tmp;
> +
> +	/* Get semaphore */

Hmm, useful comment. Anyway I think you plan to get rid of all those
while being in staging ;)?

> +	down(&(vme_irq));
> +
> +	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;

What if the interrupt comes now? I think you want to switch this block
with "Disable IRQ level" one. And take care of PCI posting (if it is not
enough that INTEO reaches the device by a consequent INTEN read).

> +	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
> +	tsi148_bridge->irq[level - 1].count--;
> +
> +	/* Disable IRQ level */
> +	if (tsi148_bridge->irq[level - 1].count == 0) {
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
> +		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +	}
> +
> +	/* Release semaphore */
> +	up(&(vme_irq));
> +}


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

* Re: [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver
  2009-08-03 22:50     ` Jiri Slaby
@ 2009-08-03 22:55       ` Jiri Slaby
  2009-08-05 16:33       ` [PATCH] Staging: Correct tsi-148 VME interrupt free routine Martyn Welch
  1 sibling, 0 replies; 65+ messages in thread
From: Jiri Slaby @ 2009-08-03 22:55 UTC (permalink / raw)
  To: martyn.welch; +Cc: Greg K-H, linux-kernel, devel

On 08/04/2009 12:50 AM, Jiri Slaby wrote:
> On 08/03/2009 11:01 PM, Greg K-H wrote:
>> +	down(&(vme_irq));
>> +
>> +	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
> 
> What if the interrupt comes now? 

On the second though, actually nothing worse than "Spurilous VME
interrupt" (if some arch doesn't reorder func and priv_data
assignments). It's not that bad, but still worth fixing.

> I think you want to switch this block
> with "Disable IRQ level" one. And take care of PCI posting (if it is not
> enough that INTEO reaches the device by a consequent INTEN read).
> 
>> +	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
>> +	tsi148_bridge->irq[level - 1].count--;
>> +
>> +	/* Disable IRQ level */
>> +	if (tsi148_bridge->irq[level - 1].count == 0) {
>> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
>> +		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
>> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
>> +
>> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
>> +		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
>> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
>> +	}
>> +
>> +	/* Release semaphore */
>> +	up(&(vme_irq));
>> +}
> 


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

* Re: [patch 3/5] Staging: vme: add Universe I/II bridge driver
  2009-08-03 21:01   ` [patch 3/5] Staging: vme: add Universe I/II bridge driver Greg K-H
@ 2009-08-03 23:00     ` Jiri Slaby
  0 siblings, 0 replies; 65+ messages in thread
From: Jiri Slaby @ 2009-08-03 23:00 UTC (permalink / raw)
  To: martyn.welch; +Cc: Greg K-H, linux-kernel, devel

On 08/03/2009 11:01 PM, Greg K-H wrote:
> +int uni_init(void)
> +{
...
> +	result =
> +	    request_irq(vmechip_irq, uni_irqhandler, IRQF_SHARED | IRQF_DISABLED,

DISABLED on SHARED is not guaranteed. You must specify one or the other.

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

* Re: [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
                     ` (4 preceding siblings ...)
  2009-08-03 21:01   ` [patch 5/5] Staging: vme: add TODO file Greg K-H
@ 2009-08-04  7:56   ` Martyn Welch
  2009-08-08 22:25   ` Emilio G. Cota
  6 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-04  7:56 UTC (permalink / raw)
  To: Greg K-H; +Cc: linux-kernel, devel

Greg K-H wrote:
> Hi all,
>
> Here's 5 patches from Martyn Welch that adds VME Bus support to the
> kernel.
>
> Martyn took the time and merged the three different existing
> implementations for Linux together into something that works, and got
> the proper legal agreements from all of the affected parties to release
> the code properly.
>   
Just for the record - I realized that two were a forks of the 
vmelinux.org codebase. I picked the most developed fork, the original 
having not been ported to the 2.6.x tree. I have also used the other 
fork in the past, I don't believe there is much if anything that is 
worth taking from that.

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* [PATCH] Staging: Correct tsi-148 VME interrupt free routine
  2009-08-03 22:50     ` Jiri Slaby
  2009-08-03 22:55       ` Jiri Slaby
@ 2009-08-05 16:33       ` Martyn Welch
  2009-08-05 16:38         ` [PATCH v2] " Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-05 16:33 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, devel, jirislaby

As identified by Jiri, the VME interrupt free routine removes the service
routine before disabling the interrupt. Re-order operations to be performed in
the reverse of the request routine.
---

Jiri: Thanks for the feedback.

Martyn

 drivers/staging/vme/bridges/vme_tsi148.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c
index ad6fc2d..cdc2919 100644
--- a/drivers/staging/vme/bridges/vme_tsi148.c
+++ b/drivers/staging/vme/bridges/vme_tsi148.c
@@ -484,21 +484,22 @@ void tsi148_free_irq(int level, int statid)
 	/* Get semaphore */
 	down(&(vme_irq));
 
-	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
-	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
 	tsi148_bridge->irq[level - 1].count--;
 
-	/* Disable IRQ level */
+	/* Disable IRQ level if no more interrupts attached at this level*/
 	if (tsi148_bridge->irq[level - 1].count == 0) {
-		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
-		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
-		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
-
 		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
 		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
 		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
+		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
 	}
 
+	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
+	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
+
 	/* Release semaphore */
 	up(&(vme_irq));
 }

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

* [PATCH v2] Staging: Correct tsi-148 VME interrupt free routine
  2009-08-05 16:33       ` [PATCH] Staging: Correct tsi-148 VME interrupt free routine Martyn Welch
@ 2009-08-05 16:38         ` Martyn Welch
  2009-08-05 21:53           ` Jiri Slaby
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-05 16:38 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, devel, jirislaby

As identified by Jiri, the VME interrupt free routine removes the service
routine before disabling the interrupt. Re-order operations to be performed in
the reverse of the request routine.

Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
---

v2: Remember "Signed-off-by"...

 drivers/staging/vme/bridges/vme_tsi148.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c
index ad6fc2d..cdc2919 100644
--- a/drivers/staging/vme/bridges/vme_tsi148.c
+++ b/drivers/staging/vme/bridges/vme_tsi148.c
@@ -484,21 +484,22 @@ void tsi148_free_irq(int level, int statid)
 	/* Get semaphore */
 	down(&(vme_irq));
 
-	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
-	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
 	tsi148_bridge->irq[level - 1].count--;
 
-	/* Disable IRQ level */
+	/* Disable IRQ level if no more interrupts attached at this level*/
 	if (tsi148_bridge->irq[level - 1].count == 0) {
-		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
-		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
-		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
-
 		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
 		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
 		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
+
+		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
+		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
+		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
 	}
 
+	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
+	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
+
 	/* Release semaphore */
 	up(&(vme_irq));
 }

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

* Re: [PATCH v2] Staging: Correct tsi-148 VME interrupt free routine
  2009-08-05 16:38         ` [PATCH v2] " Martyn Welch
@ 2009-08-05 21:53           ` Jiri Slaby
  2009-08-06  7:20             ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Jiri Slaby @ 2009-08-05 21:53 UTC (permalink / raw)
  To: Martyn Welch; +Cc: gregkh, linux-kernel, devel

On 08/05/2009 06:38 PM, Martyn Welch wrote:
>  	if (tsi148_bridge->irq[level - 1].count == 0) {
> -		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> -		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
> -		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> -
>  		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
>  		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
>  		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);

I have no idea what the registers do and I suppose it's behind some PCI
bridge anywhere. If it is not true, ignore the further.

Is it OK that the second write to INTEO doesn't reach the device before
you set func to NULL? I mean, is it enough to prevent the interrupt
raising only by twiddling INTEN? Otherwise you need to put some read
right here to push non-completed writes on bridges (flush posted
writes). (I mentioned this in the former mail too.)

Otherwise looks good.

>  	}
>  
> +	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
> +	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
> +
>  	/* Release semaphore */
>  	up(&(vme_irq));
>  }


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

* Re: [PATCH v2] Staging: Correct tsi-148 VME interrupt free routine
  2009-08-05 21:53           ` Jiri Slaby
@ 2009-08-06  7:20             ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-06  7:20 UTC (permalink / raw)
  To: Jiri Slaby; +Cc: gregkh, linux-kernel, devel

Jiri Slaby wrote:
> On 08/05/2009 06:38 PM, Martyn Welch wrote:
>   
>>  	if (tsi148_bridge->irq[level - 1].count == 0) {
>> -		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
>> -		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
>> -		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
>> -
>>  		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
>>  		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
>>  		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
>> +
>> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
>> +		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
>> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
>>     
>
> I have no idea what the registers do and I suppose it's behind some PCI
> bridge anywhere. If it is not true, ignore the further.
>
> Is it OK that the second write to INTEO doesn't reach the device before
> you set func to NULL? I mean, is it enough to prevent the interrupt
> raising only by twiddling INTEN? Otherwise you need to put some read
> right here to push non-completed writes on bridges (flush posted
> writes). (I mentioned this in the former mail too.)
>   

Yes, this is fine:

TSI148_LCSR_INTEN = Interrupt Enable
TSI148_LCSR_INTEO = Interrupt Output Enable

If an interrupt source is completely disabled, it doesn't matter whether or not it is stopped from generating a PCI interrupt.

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux
  2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
                     ` (5 preceding siblings ...)
  2009-08-04  7:56   ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Martyn Welch
@ 2009-08-08 22:25   ` Emilio G. Cota
  6 siblings, 0 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-08 22:25 UTC (permalink / raw)
  To: Greg K-H; +Cc: linux-kernel, devel, Martyn Welch, Sebastien Dugue

Greg K-H wrote:
> Hi all,
> 
> Here's 5 patches from Martyn Welch that adds VME Bus support to the
> kernel.
> 
> Martyn took the time and merged the three different existing
> implementations for Linux together into something that works, and got
> the proper legal agreements from all of the affected parties to release
> the code properly.
> 
> I'd like to thank him for taking the time to do this work, it is much
> appreciated.
> 
> These patches all add the code under the drivers/staging/ directory, as
> there is still a number of things that needs to be done to clean up the
> code to get it into "proper" mergable state, but it's a great first cut.
> 
> Thanks again Martyn!
> 
> greg k-h

whoops just spotted this.. I had allocated next month for working
on this, i.e. getting VME support into the kernel.

I work at CERN where we have hundreds of VME crates; we're
currently moving from LynxOS to real-time linux. The VME chipset
we're using is the tsi148, for which Sebastien Dugue wrote a
driver at the beginning of the year.

I'll reply to each of the relevant patches to comment on them.
Regards,
E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
@ 2009-08-08 23:01     ` Emilio G. Cota
  2009-08-10 12:44       ` Martyn Welch
  2009-08-10 16:30       ` Greg KH
  2009-08-10 14:52     ` [PATCH] Staging: vme: fix {master,slave}_get check bug Emilio G. Cota
  1 sibling, 2 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-08 23:01 UTC (permalink / raw)
  To: Greg K-H; +Cc: linux-kernel, devel, Martyn Welch, Sebastien Dugue

A few comments after a quick glance, will need to dig deeper
though:
- semaphores? isn't it screaming for mutexes?
- some function signatures (get_whatever) pass too many
  pointers; it's rather ugly. Passing a pointer to an
  appropriate struct seems a better solution.
- vme_alloc_consistent looks pretty fishy; why don't you pass
  that responsibility to the specific master? There you obviously
  know if you're bridging over PCI or whatever. Or even better;
  why is this needed at all?
- Please explain me what all the DMA functions do; are they
  meant to be used by master or slaves?

Have a look at the interface for slaves we've got for our tsi148
driver, available at:
http://repo.or.cz/w/tsi148vmebridge.git

/* API for new drivers */
extern int vme_request_irq(unsigned int, int (*)(void *),
                           void *, const char *);
extern int vme_free_irq(unsigned int );
extern int vme_generate_interrupt(int, int, signed long);
extern struct vme_mapping* find_vme_mapping_from_addr(unsigned);
extern int vme_get_window_attr(struct vme_mapping *);
extern int vme_create_window(struct vme_mapping *);
extern int vme_destroy_window(int);
extern int vme_find_mapping(struct vme_mapping *, int);
extern int vme_release_mapping(struct vme_mapping *, int);
extern int vme_do_dma(struct vme_dma *);
extern int vme_bus_error_check(int);

That's pretty thin and it covers our slaves' needs. Do you see
anything missing there?

For masters there's no interface there because it was the
master's driver who directly provided these calls to slaves.
I had it in my to-do list to split that from the tsi148, in
the same fashion as you've done with this work.

- Note above the interrupt handler; simply needs the cookie. Also,
  shouldn't your vme_request_irq() just require the IRQ vector?

- I'd like to see the whole picture, or 'vertical slice', i.e.
  the bus interface + a master + a slave driver. How would
  the slave's driver get the addresses and sizes of the mappings,
  interrupt lines, etc. for each of the devices it controls?
  For the time being we quickly hacked an xml-based scheme to get
  this info upon installation, but it's clearly not suitable
  for mainline.

Will probably have more comments when I get some time for further
inspection.

E.

(I'll leave this here because I've Cc'ed Sebastien Dugue)
Greg K-H wrote:
> From: Martyn Welch <martyn.welch@gefanuc.com>
> 
> This framework aims to colelese, extend and improve the VME Linux
> drivers found at vmelinux.org, universe2.sourceforge.net and
> openfmi.net/frs/?group_id=144. The last 2 drivers appear to be forks of
> the original code found at vmelinux.org though have extended the
> codebase.
> 
> Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> ---
>  drivers/staging/Kconfig          |    2 
>  drivers/staging/Makefile         |    1 
>  drivers/staging/vme/Kconfig      |   17 
>  drivers/staging/vme/Makefile     |    7 
>  drivers/staging/vme/vme.c        | 1371 +++++++++++++++++++++++++++++++++++++++
>  drivers/staging/vme/vme.h        |  153 ++++
>  drivers/staging/vme/vme_bridge.h |  249 +++++++
>  7 files changed, 1800 insertions(+)
> 
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -139,5 +139,7 @@ source "drivers/staging/udlfb/Kconfig"
>  
>  source "drivers/staging/hv/Kconfig"
>  
> +source "drivers/staging/vme/Kconfig"
> +
>  endif # !STAGING_EXCLUDE_BUILD
>  endif # STAGING
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -51,3 +51,4 @@ obj-$(CONFIG_FB_UDL)		+= udlfb/
>  obj-$(CONFIG_MSM_ADSP)		+= dream/qdsp5/ dream/smd/
>  obj-$(CONFIG_MSM_CAMERA)	+= dream/camera/
>  obj-$(CONFIG_HYPERV)		+= hv/
> +obj-$(CONFIG_VME)		+= vme/
> --- /dev/null
> +++ b/drivers/staging/vme/Kconfig
> @@ -0,0 +1,17 @@
> +#
> +# VME configuration.
> +#
> +
> +menuconfig VME
> +	tristate "VME bridge support"
> +	depends on PCI
> +	---help---
> +	  If you say Y here you get support for the VME bridge Framework.
> +
> +if VME
> +
> +#source "drivers/staging/vme/bridges/Kconfig"
> +#
> +#source "drivers/staging/vme/devices/Kconfig"
> +
> +endif # VME
> --- /dev/null
> +++ b/drivers/staging/vme/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for the VME bridge device drivers.
> +#
> +obj-$(CONFIG_VME)		+= vme.o
> +
> +#obj-y				+= bridges/
> +#obj-y				+= devices/
> --- /dev/null
> +++ b/drivers/staging/vme/vme_bridge.h
> @@ -0,0 +1,249 @@
> +#ifndef _VME_BRIDGE_H_
> +#define _VME_BRIDGE_H_
> +
> +#define VME_CRCSR_BUF_SIZE (508*1024)
> +#define VME_SLOTS_MAX 32
> +/*
> + * Resource structures
> + */
> +struct vme_master_resource {
> +	struct list_head list;
> +	struct vme_bridge *parent;
> +	/*
> +	 * We are likely to need to access the VME bus in interrupt context, so
> +	 * protect master routines with a spinlock rather than a semaphore.
> +	 */
> +	spinlock_t lock;
> +	int locked;
> +	int number;
> +	vme_address_t address_attr;
> +	vme_cycle_t cycle_attr;
> +	vme_width_t width_attr;
> +	struct resource pci_resource;	/* XXX Rename to be bus agnostic */
> +	void *kern_base;
> +};
> +
> +struct vme_slave_resource {
> +	struct list_head list;
> +	struct vme_bridge *parent;
> +	struct semaphore sem;
> +	int locked;
> +	int number;
> +	vme_address_t address_attr;
> +	vme_cycle_t cycle_attr;
> +};
> +
> +struct vme_dma_pattern {
> +	u32 pattern;
> +	vme_pattern_t type;
> +};
> +
> +struct vme_dma_pci {
> +	dma_addr_t address;
> +};
> +
> +struct vme_dma_vme {
> +	unsigned long long address;
> +	vme_address_t aspace;
> +	vme_cycle_t cycle;
> +	vme_width_t dwidth;
> +};
> +
> +struct vme_dma_list {
> +	struct list_head list;
> +	struct vme_dma_resource *parent;
> +	struct list_head entries;
> +	struct semaphore sem;
> +};
> +
> +struct vme_dma_resource {
> +	struct list_head list;
> +	struct vme_bridge *parent;
> +	struct semaphore sem;
> +	int locked;
> +	int number;
> +	struct list_head pending;
> +	struct list_head running;
> +};
> +
> +struct vme_bus_error {
> +	struct list_head list;
> +	unsigned long long address;
> +	u32 attributes;
> +};
> +
> +struct vme_callback {
> +	void (*func)(int, int, void*);
> +	void *priv_data;
> +};
> +
> +struct vme_irq {
> +	int count;
> +	struct vme_callback callback[255];
> +};
> +
> +/* Allow 16 characters for name (including null character) */
> +#define VMENAMSIZ 16
> +
> +/* This structure stores all the information about one bridge
> + * The structure should be dynamically allocated by the driver and one instance
> + * of the structure should be present for each VME chip present in the system.
> + *
> + * Currently we assume that all chips are PCI-based
> + */
> +struct vme_bridge {
> +        char name[VMENAMSIZ];
> +	int num;
> +	struct list_head master_resources;
> +	struct list_head slave_resources;
> +	struct list_head dma_resources;
> +
> +	struct list_head vme_errors;	/* List for errors generated on VME */
> +
> +	/* Bridge Info - XXX Move to private structure? */
> +	struct device *parent;	/* Generic device struct (pdev->dev for PCI) */
> +	void * base;		/* Base Address of device registers */
> +
> +	struct device dev[VME_SLOTS_MAX];	/* Device registered with
> +						 * device model on VME bus
> +						 */
> +
> +	/* Interrupt callbacks */
> +	struct vme_irq irq[7];
> +
> +	/* Slave Functions */
> +	int (*slave_get) (struct vme_slave_resource *, int *,
> +		unsigned long long *, unsigned long long *, dma_addr_t *,
> +		vme_address_t *, vme_cycle_t *);
> +	int (*slave_set) (struct vme_slave_resource *, int, unsigned long long,
> +		unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
> +
> +	/* Master Functions */
> +	int (*master_get) (struct vme_master_resource *, int *,
> +		unsigned long long *, unsigned long long *, vme_address_t *,
> +		vme_cycle_t *, vme_width_t *);
> +	int (*master_set) (struct vme_master_resource *, int,
> +		unsigned long long, unsigned long long,  vme_address_t,
> +		vme_cycle_t, vme_width_t);
> +	ssize_t (*master_read) (struct vme_master_resource *, void *, size_t,
> +		loff_t);
> +	ssize_t (*master_write) (struct vme_master_resource *, void *, size_t,
> +		loff_t);
> +	unsigned int (*master_rmw) (struct vme_master_resource *, unsigned int,
> +		unsigned int, unsigned int, loff_t);
> +
> +	/* DMA Functions */
> +	int (*dma_list_add) (struct vme_dma_list *, struct vme_dma_attr *,
> +		struct vme_dma_attr *, size_t);
> +	int (*dma_list_exec) (struct vme_dma_list *);
> +	int (*dma_list_empty) (struct vme_dma_list *);
> +
> +	/* Interrupt Functions */
> +	int (*request_irq) (int, int, void (*cback)(int, int, void*), void *);
> +	void (*free_irq) (int, int);
> +	int (*generate_irq) (int, int);
> +
> +	/* Location monitor functions */
> +	int (*lm_set) (unsigned long long, vme_address_t, vme_cycle_t);
> +	int (*lm_get) (unsigned long long *, vme_address_t *, vme_cycle_t *);
> +	int (*lm_attach) (int, void (*callback)(int));
> +	int (*lm_detach) (int);
> +
> +	/* CR/CSR space functions */
> +	int (*slot_get) (void);
> +	/* Use standard master read and write functions to access CR/CSR */
> +
> +#if 0
> +	int (*set_prefetch) (void);
> +	int (*get_prefetch) (void);
> +	int (*set_arbiter) (void);
> +	int (*get_arbiter) (void);
> +	int (*set_requestor) (void);
> +	int (*get_requestor) (void);
> +#endif
> +};
> +
> +int vme_register_bridge (struct vme_bridge *);
> +void vme_unregister_bridge (struct vme_bridge *);
> +
> +#endif /* _VME_BRIDGE_H_ */
> +
> +#if 0
> +/*
> + *  VMEbus GET INFO Arg Structure
> + */
> +struct vmeInfoCfg {
> +	int vmeSlotNum;		/*  VME slot number of interest */
> +	int boardResponded;	/* Board responded */
> +	char sysConFlag;	/*  System controller flag */
> +	int vmeControllerID;	/*  Vendor/device ID of VME bridge */
> +	int vmeControllerRev;	/*  Revision of VME bridge */
> +	char osName[8];		/*  Name of OS e.g. "Linux" */
> +	int vmeSharedDataValid;	/*  Validity of data struct */
> +	int vmeDriverRev;	/*  Revision of VME driver */
> +	unsigned int vmeAddrHi[8];	/* Address on VME bus */
> +	unsigned int vmeAddrLo[8];	/* Address on VME bus */
> +	unsigned int vmeSize[8];	/* Size on VME bus */
> +	unsigned int vmeAm[8];	/* Address modifier on VME bus */
> +	int reserved;		/* For future use */
> +};
> +typedef struct vmeInfoCfg vmeInfoCfg_t;
> +
> +/*
> + *  VMEbus Requester Arg Structure
> + */
> +struct vmeRequesterCfg {
> +	int requestLevel;	/*  Requester Bus Request Level */
> +	char fairMode;		/*  Requester Fairness Mode Indicator */
> +	int releaseMode;	/*  Requester Bus Release Mode */
> +	int timeonTimeoutTimer;	/*  Master Time-on Time-out Timer */
> +	int timeoffTimeoutTimer;	/*  Master Time-off Time-out Timer */
> +	int reserved;		/* For future use */
> +};
> +typedef struct vmeRequesterCfg vmeRequesterCfg_t;
> +
> +/*
> + *  VMEbus Arbiter Arg Structure
> + */
> +struct vmeArbiterCfg {
> +	vme_arbitration_t arbiterMode;	/*  Arbitration Scheduling Algorithm */
> +	char arbiterTimeoutFlag;	/*  Arbiter Time-out Timer Indicator */
> +	int globalTimeoutTimer;	/*  VMEbus Global Time-out Timer */
> +	char noEarlyReleaseFlag;	/*  No Early Release on BBUSY */
> +	int reserved;		/* For future use */
> +};
> +typedef struct vmeArbiterCfg vmeArbiterCfg_t;
> +
> +
> +/*
> + *  VMEbus RMW Configuration Data
> + */
> +struct vmeRmwCfg {
> +	unsigned int targetAddrU;	/*  VME Address (Upper) to trigger RMW cycle */
> +	unsigned int targetAddr;	/*  VME Address (Lower) to trigger RMW cycle */
> +	vme_address_t addrSpace;	/*  VME Address Space */
> +	int enableMask;		/*  Bit mask defining the bits of interest */
> +	int compareData;	/*  Data to be compared with the data read */
> +	int swapData;		/*  Data written to the VMEbus on success */
> +	int maxAttempts;	/*  Maximum times to try */
> +	int numAttempts;	/*  Number of attempts before success */
> +	int reserved;		/* For future use */
> +
> +};
> +typedef struct vmeRmwCfg vmeRmwCfg_t;
> +
> +/*
> + *  VMEbus Location Monitor Arg Structure
> + */
> +struct vmeLmCfg {
> +	unsigned int addrU;	/*  Location Monitor Address upper */
> +	unsigned int addr;	/*  Location Monitor Address lower */
> +	vme_address_t addrSpace;	/*  Address Space */
> +	int userAccessType;	/*  User/Supervisor Access Type */
> +	int dataAccessType;	/*  Data/Program Access Type */
> +	int lmWait;		/* Time to wait for access */
> +	int lmEvents;		/* Lm event mask */
> +	int reserved;		/* For future use */
> +};
> +typedef struct vmeLmCfg vmeLmCfg_t;
> +#endif
> --- /dev/null
> +++ b/drivers/staging/vme/vme.c
> @@ -0,0 +1,1371 @@
> +/*
> + * VME Bridge Framework
> + *
> + * Author: Martyn Welch <martyn.welch@gefanuc.com>
> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
> + *
> + * Based on work by Tom Armistead and Ajit Prem
> + * Copyright 2004 Motorola Inc.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/mm.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/pci.h>
> +#include <linux/poll.h>
> +#include <linux/highmem.h>
> +#include <linux/interrupt.h>
> +#include <linux/pagemap.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/syscalls.h>
> +#include <linux/semaphore.h>
> +#include <linux/spinlock.h>
> +
> +#include "vme.h"
> +#include "vme_bridge.h"
> +
> +/* Bitmask and semaphore to keep track of bridge numbers */
> +static unsigned int vme_bus_numbers;
> +DECLARE_MUTEX(vme_bus_num_sem);
> +
> +static void __exit vme_exit (void);
> +static int __init vme_init (void);
> +
> +
> +/*
> + * Find the bridge resource associated with a specific device resource
> + */
> +static struct vme_bridge *dev_to_bridge(struct device *dev)
> +{
> +	return dev->platform_data;
> +}
> +
> +/*
> + * Find the bridge that the resource is associated with.
> + */
> +static struct vme_bridge *find_bridge(struct vme_resource *resource)
> +{
> +	/* Get list to search */
> +	switch (resource->type) {
> +	case VME_MASTER:
> +		return list_entry(resource->entry, struct vme_master_resource,
> +			list)->parent;
> +		break;
> +	case VME_SLAVE:
> +		return list_entry(resource->entry, struct vme_slave_resource,
> +			list)->parent;
> +		break;
> +	case VME_DMA:
> +		return list_entry(resource->entry, struct vme_dma_resource,
> +			list)->parent;
> +		break;
> +	default:
> +		printk(KERN_ERR "Unknown resource type\n");
> +		return NULL;
> +		break;
> +	}
> +}
> +
> +/*
> + * Allocate a contiguous block of memory for use by the driver. This is used to
> + * create the buffers for the slave windows.
> + *
> + * XXX VME bridges could be available on buses other than PCI. At the momment
> + *     this framework only supports PCI devices.
> + */
> +void * vme_alloc_consistent(struct vme_resource *resource, size_t size,
> +	dma_addr_t *dma)
> +{
> +	struct vme_bridge *bridge;
> +	struct pci_dev *pdev;
> +
> +	if(resource == NULL) {
> +		printk("No resource\n");
> +		return NULL;
> +	}
> +
> +	bridge = find_bridge(resource);
> +	if(bridge == NULL) {
> +		printk("Can't find bridge\n");
> +		return NULL;
> +	}
> +
> +	/* Find pci_dev container of dev */
> +	if (bridge->parent == NULL) {
> +		printk("Dev entry NULL\n");
> +		return NULL;
> +	}
> +	pdev = container_of(bridge->parent, struct pci_dev, dev);
> +
> +	return pci_alloc_consistent(pdev, size, dma);
> +}
> +EXPORT_SYMBOL(vme_alloc_consistent);
> +
> +/*
> + * Free previously allocated contiguous block of memory.
> + *
> + * XXX VME bridges could be available on buses other than PCI. At the momment
> + *     this framework only supports PCI devices.
> + */
> +void vme_free_consistent(struct vme_resource *resource, size_t size,
> +	void *vaddr, dma_addr_t dma)
> +{
> +	struct vme_bridge *bridge;
> +	struct pci_dev *pdev;
> +
> +	if(resource == NULL) {
> +		printk("No resource\n");
> +		return;
> +	}
> +
> +	bridge = find_bridge(resource);
> +	if(bridge == NULL) {
> +		printk("Can't find bridge\n");
> +		return;
> +	}
> +
> +	/* Find pci_dev container of dev */
> +	pdev = container_of(bridge->parent, struct pci_dev, dev);
> +
> +	pci_free_consistent(pdev, size, vaddr, dma);
> +}
> +EXPORT_SYMBOL(vme_free_consistent);
> +
> +size_t vme_get_size(struct vme_resource *resource)
> +{
> +	int enabled, retval;
> +	unsigned long long base, size;
> +	dma_addr_t buf_base;
> +	vme_address_t aspace;
> +	vme_cycle_t cycle;
> +	vme_width_t dwidth;
> +
> +	switch (resource->type) {
> +	case VME_MASTER:
> +		retval = vme_master_get(resource, &enabled, &base, &size,
> +			&aspace, &cycle, &dwidth);
> +
> +		return size;
> +		break;
> +	case VME_SLAVE:
> +		retval = vme_slave_get(resource, &enabled, &base, &size,
> +			&buf_base, &aspace, &cycle);
> +
> +		return size;
> +		break;
> +	case VME_DMA:
> +		return 0;
> +		break;
> +	default:
> +		printk(KERN_ERR "Unknown resource type\n");
> +		return 0;
> +		break;
> +	}
> +}
> +EXPORT_SYMBOL(vme_get_size);
> +
> +static int vme_check_window(vme_address_t aspace, unsigned long long vme_base,
> +	unsigned long long size)
> +{
> +	int retval = 0;
> +
> +	switch (aspace) {
> +	case VME_A16:
> +		if (((vme_base + size) > VME_A16_MAX) ||
> +				(vme_base > VME_A16_MAX))
> +			retval = -EFAULT;
> +		break;
> +	case VME_A24:
> +		if (((vme_base + size) > VME_A24_MAX) ||
> +				(vme_base > VME_A24_MAX))
> +			retval = -EFAULT;
> +		break;
> +	case VME_A32:
> +		if (((vme_base + size) > VME_A32_MAX) ||
> +				(vme_base > VME_A32_MAX))
> +			retval = -EFAULT;
> +		break;
> +	case VME_A64:
> +		/*
> +		 * Any value held in an unsigned long long can be used as the
> +		 * base
> +		 */
> +		break;
> +	case VME_CRCSR:
> +		if (((vme_base + size) > VME_CRCSR_MAX) ||
> +				(vme_base > VME_CRCSR_MAX))
> +			retval = -EFAULT;
> +		break;
> +	case VME_USER1:
> +	case VME_USER2:
> +	case VME_USER3:
> +	case VME_USER4:
> +		/* User Defined */
> +		break;
> +	default:
> +		printk("Invalid address space\n");
> +		retval = -EINVAL;
> +		break;
> +	}
> +
> +	return retval;
> +}
> +
> +/*
> + * Request a slave image with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource * vme_slave_request(struct device *dev,
> +	vme_address_t address, vme_cycle_t cycle)
> +{
> +	struct vme_bridge *bridge;
> +	struct list_head *slave_pos = NULL;
> +	struct vme_slave_resource *allocated_image = NULL;
> +	struct vme_slave_resource *slave_image = NULL;
> +	struct vme_resource *resource = NULL;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		goto err_bus;
> +	}
> +
> +	/* Loop through slave resources */
> +	list_for_each(slave_pos, &(bridge->slave_resources)) {
> +		slave_image = list_entry(slave_pos,
> +			struct vme_slave_resource, list);
> +
> +		if (slave_image == NULL) {
> +			printk("Registered NULL Slave resource\n");
> +			continue;
> +		}
> +
> +		/* Find an unlocked and compatible image */
> +		down(&(slave_image->sem));
> +		if(((slave_image->address_attr & address) == address) &&
> +			((slave_image->cycle_attr & cycle) == cycle) &&
> +			(slave_image->locked == 0)) {
> +
> +			slave_image->locked = 1;
> +			up(&(slave_image->sem));
> +			allocated_image = slave_image;
> +			break;
> +		}
> +		up(&(slave_image->sem));
> +	}
> +
> +	/* No free image */
> +	if (allocated_image == NULL)
> +		goto err_image;
> +
> +	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> +	if (resource == NULL) {
> +		printk(KERN_WARNING "Unable to allocate resource structure\n");
> +		goto err_alloc;
> +	}
> +	resource->type = VME_SLAVE;
> +	resource->entry = &(allocated_image->list);
> +
> +	return resource;
> +
> +err_alloc:
> +	/* Unlock image */
> +	down(&(slave_image->sem));
> +	slave_image->locked = 0;
> +	up(&(slave_image->sem));
> +err_image:
> +err_bus:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_slave_request);
> +
> +int vme_slave_set (struct vme_resource *resource, int enabled,
> +	unsigned long long vme_base, unsigned long long size,
> +	dma_addr_t buf_base, vme_address_t aspace, vme_cycle_t cycle)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_slave_resource *image;
> +	int retval;
> +
> +	if (resource->type != VME_SLAVE) {
> +		printk("Not a slave resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_slave_resource, list);
> +
> +	if (bridge->slave_set == NULL) {
> +		printk("Function not supported\n");
> +		return -ENOSYS;
> +	}
> +
> +	if(!(((image->address_attr & aspace) == aspace) &&
> +		((image->cycle_attr & cycle) == cycle))) {
> +		printk("Invalid attributes\n");
> +		return -EINVAL;
> +	}
> +
> +	retval = vme_check_window(aspace, vme_base, size);
> +	if(retval)
> +		return retval;
> +
> +	return bridge->slave_set(image, enabled, vme_base, size, buf_base,
> +		aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_slave_set);
> +
> +int vme_slave_get (struct vme_resource *resource, int *enabled,
> +	unsigned long long *vme_base, unsigned long long *size,
> +	dma_addr_t *buf_base, vme_address_t *aspace, vme_cycle_t *cycle)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_slave_resource *image;
> +
> +	if (resource->type != VME_SLAVE) {
> +		printk("Not a slave resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_slave_resource, list);
> +
> +	if (bridge->slave_set == NULL) {
> +		printk("vme_slave_get not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->slave_get(image, enabled, vme_base, size, buf_base,
> +		aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_slave_get);
> +
> +void vme_slave_free(struct vme_resource *resource)
> +{
> +	struct vme_slave_resource *slave_image;
> +
> +	if (resource->type != VME_SLAVE) {
> +		printk("Not a slave resource\n");
> +		return;
> +	}
> +
> +	slave_image = list_entry(resource->entry, struct vme_slave_resource,
> +		list);
> +	if (slave_image == NULL) {
> +		printk("Can't find slave resource\n");
> +		return;
> +	}
> +
> +	/* Unlock image */
> +	down(&(slave_image->sem));
> +	if (slave_image->locked == 0)
> +		printk(KERN_ERR "Image is already free\n");
> +
> +	slave_image->locked = 0;
> +	up(&(slave_image->sem));
> +
> +	/* Free up resource memory */
> +	kfree(resource);
> +}
> +EXPORT_SYMBOL(vme_slave_free);
> +
> +/*
> + * Request a master image with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource * vme_master_request(struct device *dev,
> +	vme_address_t address, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	struct vme_bridge *bridge;
> +	struct list_head *master_pos = NULL;
> +	struct vme_master_resource *allocated_image = NULL;
> +	struct vme_master_resource *master_image = NULL;
> +	struct vme_resource *resource = NULL;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		goto err_bus;
> +	}
> +
> +	/* Loop through master resources */
> +	list_for_each(master_pos, &(bridge->master_resources)) {
> +		master_image = list_entry(master_pos,
> +			struct vme_master_resource, list);
> +
> +		if (master_image == NULL) {
> +			printk(KERN_WARNING "Registered NULL master resource\n");
> +			continue;
> +		}
> +
> +		/* Find an unlocked and compatible image */
> +		spin_lock(&(master_image->lock));
> +		if(((master_image->address_attr & address) == address) &&
> +			((master_image->cycle_attr & cycle) == cycle) &&
> +			((master_image->width_attr & dwidth) == dwidth) &&
> +			(master_image->locked == 0)) {
> +
> +			master_image->locked = 1;
> +			spin_unlock(&(master_image->lock));
> +			allocated_image = master_image;
> +			break;
> +		}
> +		spin_unlock(&(master_image->lock));
> +	}
> +
> +	/* Check to see if we found a resource */
> +	if (allocated_image == NULL) {
> +		printk(KERN_ERR "Can't find a suitable resource\n");
> +		goto err_image;
> +	}
> +
> +	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> +	if (resource == NULL) {
> +		printk(KERN_ERR "Unable to allocate resource structure\n");
> +		goto err_alloc;
> +	}
> +	resource->type = VME_MASTER;
> +	resource->entry = &(allocated_image->list);
> +
> +	return resource;
> +
> +	kfree(resource);
> +err_alloc:
> +	/* Unlock image */
> +	spin_lock(&(master_image->lock));
> +	master_image->locked = 0;
> +	spin_unlock(&(master_image->lock));
> +err_image:
> +err_bus:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_master_request);
> +
> +int vme_master_set (struct vme_resource *resource, int enabled,
> +	unsigned long long vme_base, unsigned long long size,
> +	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_master_resource *image;
> +	int retval;
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> +	if (bridge->master_set == NULL) {
> +		printk("vme_master_set not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if(!(((image->address_attr & aspace) == aspace) &&
> +		((image->cycle_attr & cycle) == cycle) &&
> +		((image->width_attr & dwidth) == dwidth))) {
> +		printk("Invalid attributes\n");
> +		return -EINVAL;
> +	}
> +
> +	retval = vme_check_window(aspace, vme_base, size);
> +	if(retval)
> +		return retval;
> +
> +	return bridge->master_set(image, enabled, vme_base, size, aspace,
> +		cycle, dwidth);
> +}
> +EXPORT_SYMBOL(vme_master_set);
> +
> +int vme_master_get (struct vme_resource *resource, int *enabled,
> +	unsigned long long *vme_base, unsigned long long *size,
> +	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_master_resource *image;
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> +	if (bridge->master_set == NULL) {
> +		printk("vme_master_set not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->master_get(image, enabled, vme_base, size, aspace,
> +		cycle, dwidth);
> +}
> +EXPORT_SYMBOL(vme_master_get);
> +
> +/*
> + * Read data out of VME space into a buffer.
> + */
> +ssize_t vme_master_read (struct vme_resource *resource, void *buf, size_t count,
> +	loff_t offset)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_master_resource *image;
> +	size_t length;
> +
> +	if (bridge->master_read == NULL) {
> +		printk("Reading from resource not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> +	length = vme_get_size(resource);
> +
> +	if (offset > length) {
> +		printk("Invalid Offset\n");
> +		return -EFAULT;
> +	}
> +
> +	if ((offset + count) > length)
> +		count = length - offset;
> +
> +	return bridge->master_read(image, buf, count, offset);
> +
> +}
> +EXPORT_SYMBOL(vme_master_read);
> +
> +/*
> + * Write data out to VME space from a buffer.
> + */
> +ssize_t vme_master_write (struct vme_resource *resource, void *buf,
> +	size_t count, loff_t offset)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_master_resource *image;
> +	size_t length;
> +
> +	if (bridge->master_write == NULL) {
> +		printk("Writing to resource not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> +	length = vme_get_size(resource);
> +
> +	if (offset > length) {
> +		printk("Invalid Offset\n");
> +		return -EFAULT;
> +	}
> +
> +	if ((offset + count) > length)
> +		count = length - offset;
> +
> +	return bridge->master_write(image, buf, count, offset);
> +}
> +EXPORT_SYMBOL(vme_master_write);
> +
> +/*
> + * Perform RMW cycle to provided location.
> + */
> +unsigned int vme_master_rmw (struct vme_resource *resource, unsigned int mask,
> +	unsigned int compare, unsigned int swap, loff_t offset)
> +{
> +	struct vme_bridge *bridge = find_bridge(resource);
> +	struct vme_master_resource *image;
> +
> +	if (bridge->master_rmw == NULL) {
> +		printk("Writing to resource not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return -EINVAL;
> +	}
> +
> +	image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> +	return bridge->master_rmw(image, mask, compare, swap, offset);
> +}
> +EXPORT_SYMBOL(vme_master_rmw);
> +
> +void vme_master_free(struct vme_resource *resource)
> +{
> +	struct vme_master_resource *master_image;
> +
> +	if (resource->type != VME_MASTER) {
> +		printk("Not a master resource\n");
> +		return;
> +	}
> +
> +	master_image = list_entry(resource->entry, struct vme_master_resource,
> +		list);
> +	if (master_image == NULL) {
> +		printk("Can't find master resource\n");
> +		return;
> +	}
> +
> +	/* Unlock image */
> +	spin_lock(&(master_image->lock));
> +	if (master_image->locked == 0)
> +		printk(KERN_ERR "Image is already free\n");
> +
> +	master_image->locked = 0;
> +	spin_unlock(&(master_image->lock));
> +
> +	/* Free up resource memory */
> +	kfree(resource);
> +}
> +EXPORT_SYMBOL(vme_master_free);
> +
> +/*
> + * Request a DMA controller with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource *vme_request_dma(struct device *dev)
> +{
> +	struct vme_bridge *bridge;
> +	struct list_head *dma_pos = NULL;
> +	struct vme_dma_resource *allocated_ctrlr = NULL;
> +	struct vme_dma_resource *dma_ctrlr = NULL;
> +	struct vme_resource *resource = NULL;
> +
> +	/* XXX Not checking resource attributes */
> +	printk(KERN_ERR "No VME resource Attribute tests done\n");
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		goto err_bus;
> +	}
> +
> +	/* Loop through DMA resources */
> +	list_for_each(dma_pos, &(bridge->dma_resources)) {
> +		dma_ctrlr = list_entry(dma_pos,
> +			struct vme_dma_resource, list);
> +
> +		if (dma_ctrlr == NULL) {
> +			printk("Registered NULL DMA resource\n");
> +			continue;
> +		}
> +
> +		/* Find an unlocked controller */
> +		down(&(dma_ctrlr->sem));
> +		if(dma_ctrlr->locked == 0) {
> +			dma_ctrlr->locked = 1;
> +			up(&(dma_ctrlr->sem));
> +			allocated_ctrlr = dma_ctrlr;
> +			break;
> +		}
> +		up(&(dma_ctrlr->sem));
> +	}
> +
> +	/* Check to see if we found a resource */
> +	if (allocated_ctrlr == NULL)
> +		goto err_ctrlr;
> +
> +	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> +	if (resource == NULL) {
> +		printk(KERN_WARNING "Unable to allocate resource structure\n");
> +		goto err_alloc;
> +	}
> +	resource->type = VME_DMA;
> +	resource->entry = &(allocated_ctrlr->list);
> +
> +	return resource;
> +
> +err_alloc:
> +	/* Unlock image */
> +	down(&(dma_ctrlr->sem));
> +	dma_ctrlr->locked = 0;
> +	up(&(dma_ctrlr->sem));
> +err_ctrlr:
> +err_bus:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_request_dma);
> +
> +/*
> + * Start new list
> + */
> +struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
> +{
> +	struct vme_dma_resource *ctrlr;
> +	struct vme_dma_list *dma_list;
> +
> +	if (resource->type != VME_DMA) {
> +		printk("Not a DMA resource\n");
> +		return NULL;
> +	}
> +
> +	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
> +
> +	dma_list = (struct vme_dma_list *)kmalloc(
> +		sizeof(struct vme_dma_list), GFP_KERNEL);
> +	if(dma_list == NULL) {
> +		printk("Unable to allocate memory for new dma list\n");
> +		return NULL;
> +	}
> +	INIT_LIST_HEAD(&(dma_list->entries));
> +	dma_list->parent = ctrlr;
> +	init_MUTEX(&(dma_list->sem));
> +
> +	return dma_list;
> +}
> +EXPORT_SYMBOL(vme_new_dma_list);
> +
> +/*
> + * Create "Pattern" type attributes
> + */
> +struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern,
> +	vme_pattern_t type)
> +{
> +	struct vme_dma_attr *attributes;
> +	struct vme_dma_pattern *pattern_attr;
> +
> +	attributes = (struct vme_dma_attr *)kmalloc(
> +		sizeof(struct vme_dma_attr), GFP_KERNEL);
> +	if(attributes == NULL) {
> +		printk("Unable to allocate memory for attributes structure\n");
> +		goto err_attr;
> +	}
> +
> +	pattern_attr = (struct vme_dma_pattern *)kmalloc(
> +		sizeof(struct vme_dma_pattern), GFP_KERNEL);
> +	if(pattern_attr == NULL) {
> +		printk("Unable to allocate memory for pattern attributes\n");
> +		goto err_pat;
> +	}
> +
> +	attributes->type = VME_DMA_PATTERN;
> +	attributes->private = (void *)pattern_attr;
> +
> +	pattern_attr->pattern = pattern;
> +	pattern_attr->type = type;
> +
> +	return attributes;
> +
> +	kfree(pattern_attr);
> +err_pat:
> +	kfree(attributes);
> +err_attr:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_pattern_attribute);
> +
> +/*
> + * Create "PCI" type attributes
> + */
> +struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
> +{
> +	struct vme_dma_attr *attributes;
> +	struct vme_dma_pci *pci_attr;
> +
> +	/* XXX Run some sanity checks here */
> +
> +	attributes = (struct vme_dma_attr *)kmalloc(
> +		sizeof(struct vme_dma_attr), GFP_KERNEL);
> +	if(attributes == NULL) {
> +		printk("Unable to allocate memory for attributes structure\n");
> +		goto err_attr;
> +	}
> +
> +	pci_attr = (struct vme_dma_pci *)kmalloc(sizeof(struct vme_dma_pci),
> +		GFP_KERNEL);
> +	if(pci_attr == NULL) {
> +		printk("Unable to allocate memory for pci attributes\n");
> +		goto err_pci;
> +	}
> +
> +
> +
> +	attributes->type = VME_DMA_PCI;
> +	attributes->private = (void *)pci_attr;
> +
> +	pci_attr->address = address;
> +
> +	return attributes;
> +
> +	kfree(pci_attr);
> +err_pci:
> +	kfree(attributes);
> +err_attr:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_pci_attribute);
> +
> +/*
> + * Create "VME" type attributes
> + */
> +struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
> +	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	struct vme_dma_attr *attributes;
> +	struct vme_dma_vme *vme_attr;
> +
> +	/* XXX Run some sanity checks here */
> +
> +	attributes = (struct vme_dma_attr *)kmalloc(
> +		sizeof(struct vme_dma_attr), GFP_KERNEL);
> +	if(attributes == NULL) {
> +		printk("Unable to allocate memory for attributes structure\n");
> +		goto err_attr;
> +	}
> +
> +	vme_attr = (struct vme_dma_vme *)kmalloc(sizeof(struct vme_dma_vme),
> +		GFP_KERNEL);
> +	if(vme_attr == NULL) {
> +		printk("Unable to allocate memory for vme attributes\n");
> +		goto err_vme;
> +	}
> +
> +	attributes->type = VME_DMA_VME;
> +	attributes->private = (void *)vme_attr;
> +
> +	vme_attr->address = address;
> +	vme_attr->aspace = aspace;
> +	vme_attr->cycle = cycle;
> +	vme_attr->dwidth = dwidth;
> +
> +	return attributes;
> +
> +	kfree(vme_attr);
> +err_vme:
> +	kfree(attributes);
> +err_attr:
> +	return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_vme_attribute);
> +
> +/*
> + * Free attribute
> + */
> +void vme_dma_free_attribute(struct vme_dma_attr *attributes)
> +{
> +	kfree(attributes->private);
> +	kfree(attributes);
> +}
> +EXPORT_SYMBOL(vme_dma_free_attribute);
> +
> +int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
> +	struct vme_dma_attr *dest, size_t count)
> +{
> +	struct vme_bridge *bridge = list->parent->parent;
> +	int retval;
> +
> +	if (bridge->dma_list_add == NULL) {
> +		printk("Link List DMA generation not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (down_trylock(&(list->sem))) {
> +		printk("Link List already submitted\n");
> +		return -EINVAL;
> +	}
> +
> +	retval = bridge->dma_list_add(list, src, dest, count);
> +
> +	up(&(list->sem));
> +
> +	return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_add);
> +
> +int vme_dma_list_exec(struct vme_dma_list *list)
> +{
> +	struct vme_bridge *bridge = list->parent->parent;
> +	int retval;
> +
> +	if (bridge->dma_list_exec == NULL) {
> +		printk("Link List DMA execution not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	down(&(list->sem));
> +
> +	retval = bridge->dma_list_exec(list);
> +
> +	up(&(list->sem));
> +
> +	return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_exec);
> +
> +int vme_dma_list_free(struct vme_dma_list *list)
> +{
> +	struct vme_bridge *bridge = list->parent->parent;
> +	int retval;
> +
> +	if (bridge->dma_list_empty == NULL) {
> +		printk("Emptying of Link Lists not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (down_trylock(&(list->sem))) {
> +		printk("Link List in use\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Empty out all of the entries from the dma list. We need to go to the
> +	 * low level driver as dma entries are driver specific.
> +	 */
> +	retval = bridge->dma_list_empty(list);
> +	if (retval) {
> +		printk("Unable to empty link-list entries\n");
> +		up(&(list->sem));
> +		return retval;
> +	}
> +	up(&(list->sem));
> +	kfree(list);
> +
> +	return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_free);
> +
> +int vme_dma_free(struct vme_resource *resource)
> +{
> +	struct vme_dma_resource *ctrlr;
> +
> +	if (resource->type != VME_DMA) {
> +		printk("Not a DMA resource\n");
> +		return -EINVAL;
> +	}
> +
> +	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
> +
> +	if (down_trylock(&(ctrlr->sem))) {
> +		printk("Resource busy, can't free\n");
> +		return -EBUSY;
> +	}
> +
> +	if (!(list_empty(&(ctrlr->pending)) && list_empty(&(ctrlr->running)))) {
> +		printk("Resource still processing transfers\n");
> +		up(&(ctrlr->sem));
> +		return -EBUSY;
> +	}
> +
> +	ctrlr->locked = 0;
> +
> +	up(&(ctrlr->sem));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(vme_dma_free);
> +
> +int vme_request_irq(struct device *dev, int level, int statid,
> +	void (*callback)(int level, int vector, void *priv_data),
> +	void *priv_data)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if((level < 1) || (level > 7)) {
> +		printk(KERN_WARNING "Invalid interrupt level\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->request_irq == NULL) {
> +		printk("Registering interrupts not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->request_irq(level, statid, callback, priv_data);
> +}
> +EXPORT_SYMBOL(vme_request_irq);
> +
> +void vme_free_irq(struct device *dev, int level, int statid)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return;
> +	}
> +
> +	if((level < 1) || (level > 7)) {
> +		printk(KERN_WARNING "Invalid interrupt level\n");
> +		return;
> +	}
> +
> +	if (bridge->free_irq == NULL) {
> +		printk("Freeing interrupts not supported\n");
> +		return;
> +	}
> +
> +	bridge->free_irq(level, statid);
> +}
> +EXPORT_SYMBOL(vme_free_irq);
> +
> +int vme_generate_irq(struct device *dev, int level, int statid)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if((level < 1) || (level > 7)) {
> +		printk(KERN_WARNING "Invalid interrupt level\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->generate_irq == NULL) {
> +		printk("Interrupt generation not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->generate_irq(level, statid);
> +}
> +EXPORT_SYMBOL(vme_generate_irq);
> +
> +int vme_lm_set(struct device *dev, unsigned long long lm_base, vme_address_t aspace,
> +	vme_cycle_t cycle)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->lm_set == NULL) {
> +		printk("vme_lm_set not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->lm_set(lm_base, aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_lm_set);
> +
> +int vme_lm_get(struct device *dev, unsigned long long *lm_base, vme_address_t *aspace,
> +	vme_cycle_t *cycle)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->lm_get == NULL) {
> +		printk("vme_lm_get not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->lm_get(lm_base, aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_lm_get);
> +
> +int vme_lm_attach(struct device *dev, int monitor, void (*callback)(int))
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->lm_attach == NULL) {
> +		printk("vme_lm_attach not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->lm_attach(monitor, callback);
> +}
> +EXPORT_SYMBOL(vme_lm_attach);
> +
> +int vme_lm_detach(struct device *dev, int monitor)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(dev);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->lm_detach == NULL) {
> +		printk("vme_lm_detach not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->lm_detach(monitor);
> +}
> +EXPORT_SYMBOL(vme_lm_detach);
> +
> +int vme_slot_get(struct device *bus)
> +{
> +	struct vme_bridge *bridge;
> +
> +	bridge = dev_to_bridge(bus);
> +	if (bridge == NULL) {
> +		printk(KERN_ERR "Can't find VME bus\n");
> +		return -EINVAL;
> +	}
> +
> +	if (bridge->slot_get == NULL) {
> +		printk("vme_slot_get not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return bridge->slot_get();
> +}
> +EXPORT_SYMBOL(vme_slot_get);
> +
> +
> +/* - Bridge Registration --------------------------------------------------- */
> +
> +static int vme_alloc_bus_num(void)
> +{
> +	int i;
> +
> +	down(&vme_bus_num_sem);
> +	for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) {
> +		if (((vme_bus_numbers >> i) & 0x1) == 0) {
> +			vme_bus_numbers |= (0x1 << i);
> +			break;
> +		}
> +	}
> +	up(&vme_bus_num_sem);
> +
> +	return i;
> +}
> +
> +static void vme_free_bus_num(int bus)
> +{
> +	down(&vme_bus_num_sem);
> +	vme_bus_numbers |= ~(0x1 << bus);
> +	up(&vme_bus_num_sem);
> +}
> +
> +int vme_register_bridge (struct vme_bridge *bridge)
> +{
> +	struct device *dev;
> +	int retval;
> +	int i;
> +
> +	bridge->num = vme_alloc_bus_num();
> +
> +	/* This creates 32 vme "slot" devices. This equates to a slot for each
> +	 * ID available in a system conforming to the ANSI/VITA 1-1994
> +	 * specification.
> +	 */
> +	for (i = 0; i < VME_SLOTS_MAX; i++) {
> +		dev = &(bridge->dev[i]);
> +		memset(dev, 0, sizeof(struct device));
> +
> +		dev->parent = bridge->parent;
> +		dev->bus = &(vme_bus_type);
> +		/*
> +		 * We save a pointer to the bridge in platform_data so that we
> +		 * can get to it later. We keep driver_data for use by the
> +		 * driver that binds against the slot
> +		 */
> +		dev->platform_data = bridge;
> +		dev_set_name(dev, "vme-%x.%x", bridge->num, i + 1);
> +
> +		retval = device_register(dev);
> +		if(retval)
> +			goto err_reg;
> +	}
> +
> +	return retval;
> +
> +	i = VME_SLOTS_MAX;
> +err_reg:
> +	while (i > -1) {
> +		dev = &(bridge->dev[i]);
> +		device_unregister(dev);
> +	}
> +	vme_free_bus_num(bridge->num);
> +	return retval;
> +}
> +EXPORT_SYMBOL(vme_register_bridge);
> +
> +void vme_unregister_bridge (struct vme_bridge *bridge)
> +{
> +	int i;
> +	struct device *dev;
> +
> +
> +	for (i = 0; i < VME_SLOTS_MAX; i++) {
> +		dev = &(bridge->dev[i]);
> +		device_unregister(dev);
> +	}
> +	vme_free_bus_num(bridge->num);
> +}
> +EXPORT_SYMBOL(vme_unregister_bridge);
> +
> +
> +/* - Driver Registration --------------------------------------------------- */
> +
> +int vme_register_driver (struct vme_driver *drv)
> +{
> +	drv->driver.name = drv->name;
> +	drv->driver.bus = &vme_bus_type;
> +
> +	return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL(vme_register_driver);
> +
> +void vme_unregister_driver (struct vme_driver *drv)
> +{
> +	driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL(vme_unregister_driver);
> +
> +/* - Bus Registration ------------------------------------------------------ */
> +
> +int vme_calc_slot(struct device *dev)
> +{
> +	struct vme_bridge *bridge;
> +	int num;
> +
> +	bridge = dev_to_bridge(dev);
> +
> +	/* Determine slot number */
> +	num = 0;
> +	while(num < VME_SLOTS_MAX) {
> +		if(&(bridge->dev[num]) == dev) {
> +			break;
> +		}
> +		num++;
> +	}
> +	if (num == VME_SLOTS_MAX) {
> +		dev_err(dev, "Failed to identify slot\n");
> +		num = 0;
> +		goto err_dev;
> +	}
> +	num++;
> +
> +err_dev:
> +	return num;
> +}
> +
> +static struct vme_driver *dev_to_vme_driver(struct device *dev)
> +{
> +	if(dev->driver == NULL)
> +		printk("Bugger dev->driver is NULL\n");
> +
> +	return container_of(dev->driver, struct vme_driver, driver);
> +}
> +
> +static int vme_bus_match(struct device *dev, struct device_driver *drv)
> +{
> +	struct vme_bridge *bridge;
> +	struct vme_driver *driver;
> +	int i, num;
> +
> +	bridge = dev_to_bridge(dev);
> +	driver = container_of(drv, struct vme_driver, driver);
> +
> +	num = vme_calc_slot(dev);
> +	if (!num)
> +		goto err_dev;
> +
> +	if (driver->bind_table == NULL) {
> +		dev_err(dev, "Bind table NULL\n");
> +		goto err_table;
> +	}
> +
> +	i = 0;
> +	while((driver->bind_table[i].bus != 0) ||
> +		(driver->bind_table[i].slot != 0)) {
> +
> +		if ((bridge->num == driver->bind_table[i].bus) &&
> +			(num == driver->bind_table[i].slot))
> +			return 1;
> +		i++;
> +	}
> +
> +err_dev:
> +err_table:
> +	return 0;
> +}
> +
> +static int vme_bus_probe(struct device *dev)
> +{
> +	struct vme_bridge *bridge;
> +	struct vme_driver *driver;
> +	int retval = -ENODEV;
> +
> +	driver = dev_to_vme_driver(dev);
> +	bridge = dev_to_bridge(dev);
> +
> +	if(driver->probe != NULL) {
> +		retval = driver->probe(dev, bridge->num, vme_calc_slot(dev));
> +	}
> +
> +	return retval;
> +}
> +
> +static int vme_bus_remove(struct device *dev)
> +{
> +	struct vme_bridge *bridge;
> +	struct vme_driver *driver;
> +	int retval = -ENODEV;
> +
> +	driver = dev_to_vme_driver(dev);
> +	bridge = dev_to_bridge(dev);
> +
> +	if(driver->remove != NULL) {
> +		retval = driver->remove(dev, bridge->num, vme_calc_slot(dev));
> +	}
> +
> +	return retval;
> +}
> +
> +struct bus_type vme_bus_type = {
> +	.name = "vme",
> +	.match = vme_bus_match,
> +	.probe = vme_bus_probe,
> +	.remove = vme_bus_remove,
> +};
> +EXPORT_SYMBOL(vme_bus_type);
> +
> +static int __init vme_init (void)
> +{
> +	return bus_register(&vme_bus_type);
> +}
> +
> +static void __exit vme_exit (void)
> +{
> +	bus_unregister(&vme_bus_type);
> +}
> +
> +MODULE_DESCRIPTION("VME bridge driver framework");
> +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
> +MODULE_LICENSE("GPL");
> +
> +module_init(vme_init);
> +module_exit(vme_exit);
> --- /dev/null
> +++ b/drivers/staging/vme/vme.h
> @@ -0,0 +1,153 @@
> +#ifndef _VME_H_
> +#define _VME_H_
> +
> +/* Resource Type */
> +enum vme_resource_type {
> +	VME_MASTER,
> +	VME_SLAVE,
> +	VME_DMA
> +};
> +
> +/* VME Address Spaces */
> +typedef u32 vme_address_t;
> +#define VME_A16		0x1
> +#define VME_A24		0x2
> +#define	VME_A32		0x4
> +#define VME_A64		0x8
> +#define VME_CRCSR	0x10
> +#define VME_USER1	0x20
> +#define VME_USER2	0x40
> +#define VME_USER3	0x80
> +#define VME_USER4	0x100
> +
> +#define VME_A16_MAX	0x10000ULL
> +#define VME_A24_MAX	0x1000000ULL
> +#define VME_A32_MAX	0x100000000ULL
> +#define VME_A64_MAX	0x10000000000000000ULL
> +#define VME_CRCSR_MAX	0x1000000ULL
> +
> +
> +/* VME Cycle Types */
> +typedef u32 vme_cycle_t;
> +#define VME_SCT		0x1
> +#define VME_BLT		0x2
> +#define VME_MBLT	0x4
> +#define VME_2eVME	0x8
> +#define VME_2eSST	0x10
> +#define VME_2eSSTB	0x20
> +
> +#define VME_2eSST160	0x100
> +#define VME_2eSST267	0x200
> +#define VME_2eSST320	0x400
> +
> +#define	VME_SUPER	0x1000
> +#define	VME_USER	0x2000
> +#define	VME_PROG	0x4000
> +#define	VME_DATA	0x8000
> +
> +/* VME Data Widths */
> +typedef u32 vme_width_t;
> +#define VME_D8		0x1
> +#define VME_D16		0x2
> +#define VME_D32		0x4
> +#define VME_D64		0x8
> +
> +/* Arbitration Scheduling Modes */
> +typedef u32 vme_arbitration_t;
> +#define VME_R_ROBIN_MODE	0x1
> +#define VME_PRIORITY_MODE	0x2
> +
> +typedef u32 vme_dma_t;
> +#define VME_DMA_PATTERN			(1<<0)
> +#define VME_DMA_PCI			(1<<1)
> +#define VME_DMA_VME			(1<<2)
> +
> +typedef u32 vme_pattern_t;
> +#define VME_DMA_PATTERN_BYTE		(1<<0)
> +#define VME_DMA_PATTERN_WORD		(1<<1)
> +#define VME_DMA_PATTERN_INCREMENT	(1<<2)
> +
> +struct vme_dma_attr {
> +	vme_dma_t type;
> +	void *private;
> +};
> +
> +struct vme_resource {
> +	enum vme_resource_type type;
> +	struct list_head *entry;
> +};
> +
> +extern struct bus_type vme_bus_type;
> +
> +struct vme_device_id {
> +	int bus;
> +	int slot;
> +};
> +
> +struct vme_driver {
> +	struct list_head node;
> +	char *name;
> +	const struct vme_device_id *bind_table;
> +	int (*probe)  (struct device *, int, int);
> +	int (*remove) (struct device *, int, int);
> +	void (*shutdown) (void);
> +	struct device_driver    driver;
> +};
> +
> +void * vme_alloc_consistent(struct vme_resource *, size_t, dma_addr_t *);
> +void vme_free_consistent(struct vme_resource *, size_t,  void *,
> +	dma_addr_t);
> +
> +size_t vme_get_size(struct vme_resource *);
> +
> +struct vme_resource * vme_slave_request(struct device *, vme_address_t, vme_cycle_t);
> +int vme_slave_set (struct vme_resource *, int, unsigned long long,
> +	unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
> +int vme_slave_get (struct vme_resource *, int *, unsigned long long *,
> +	unsigned long long *, dma_addr_t *, vme_address_t *, vme_cycle_t *);
> +void vme_slave_free(struct vme_resource *);
> +
> +struct vme_resource * vme_master_request(struct device *, vme_address_t, vme_cycle_t,
> +	vme_width_t);
> +int vme_master_set (struct vme_resource *, int, unsigned long long,
> +	unsigned long long, vme_address_t, vme_cycle_t, vme_width_t);
> +int vme_master_get (struct vme_resource *, int *, unsigned long long *,
> +	unsigned long long *, vme_address_t *, vme_cycle_t *, vme_width_t *);
> +ssize_t vme_master_read(struct vme_resource *, void *, size_t, loff_t);
> +ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t);
> +unsigned int vme_master_rmw (struct vme_resource *, unsigned int, unsigned int,
> +	unsigned int, loff_t);
> +void vme_master_free(struct vme_resource *);
> +
> +struct vme_resource *vme_request_dma(struct device *);
> +struct vme_dma_list *vme_new_dma_list(struct vme_resource *);
> +struct vme_dma_attr *vme_dma_pattern_attribute(u32, vme_pattern_t);
> +struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t);
> +struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long, vme_address_t,
> +	vme_cycle_t, vme_width_t);
> +void vme_dma_free_attribute(struct vme_dma_attr *);
> +int vme_dma_list_add(struct vme_dma_list *, struct vme_dma_attr *,
> +	struct vme_dma_attr *, size_t);
> +int vme_dma_list_exec(struct vme_dma_list *);
> +int vme_dma_list_free(struct vme_dma_list *);
> +int vme_dma_free(struct vme_resource *);
> +
> +int vme_request_irq(struct device *, int, int,
> +	void (*callback)(int, int, void *), void *);
> +void vme_free_irq(struct device *, int, int);
> +int vme_generate_irq(struct device *, int, int);
> +
> +int vme_lm_set(struct device *, unsigned long long, vme_address_t, vme_cycle_t);
> +int vme_lm_get(struct device *, unsigned long long *, vme_address_t *,
> +	vme_cycle_t *);
> +int vme_lm_attach(struct device *, int, void (*callback)(int));
> +int vme_lm_detach(struct device *, int);
> +
> +int vme_slot_get(struct device *);
> +
> +int vme_register_driver (struct vme_driver *);
> +void vme_unregister_driver (struct vme_driver *);
> +
> +
> +#endif /* _VME_H_ */
> +
> 
> 

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-03 21:01   ` [patch 2/5] Staging: vme: add VME userspace driver Greg K-H
@ 2009-08-08 23:22     ` Emilio G. Cota
  2009-08-09 12:17       ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-08 23:22 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

I remember we had a look at the vmelinux interface, but it really
was so limited that we decided to get rid of it altogether. Do you
really think it's worth bringing it back to life? Since it's never
been merged into the kernel, why don't we just get it right this time
and forget about it?

Instead of using that we implemented a heretic IOCTL-based
interface for user-space; at least with it you could create a
driver (with no interrupt support) for a device in user-space.
We actually don't use it, but well, just to get inspiration, here it
goes:

/**
 * \name Window management ioctl numbers
 */
/** Get a physical window attributes */
#define VME_IOCTL_GET_WINDOW_ATTR       _IOWR('V', 0, struct
vme_mapping)
/** Create a physical window */
#define VME_IOCTL_CREATE_WINDOW         _IOW( 'V', 1, struct
vme_mapping)
/** Destroy a physical window */
#define VME_IOCTL_DESTROY_WINDOW        _IOW( 'V', 2, int)
/** Create a mapping over a physical window */
#define VME_IOCTL_FIND_MAPPING          _IOWR('V', 3, struct
vme_mapping)
/** Remove a mapping */
#define VME_IOCTL_RELEASE_MAPPING       _IOW( 'V', 4, struct
vme_mapping)
/** Get the create on find failed flag */
#define VME_IOCTL_GET_CREATE_ON_FIND_FAIL       _IOR( 'V', 5, unsigned
int)
/** Set the create on find failed flag */
#define VME_IOCTL_SET_CREATE_ON_FIND_FAIL       _IOW( 'V', 6, unsigned
int)
/** Get the destroy on remove flag */
#define VME_IOCTL_GET_DESTROY_ON_REMOVE _IOR( 'V', 7, unsigned int)
/** Set the destroy on remove flag */
#define VME_IOCTL_SET_DESTROY_ON_REMOVE _IOW( 'V', 8, unsigned int)
/** Get bus error status */
#define VME_IOCTL_GET_BUS_ERROR         _IOR( 'V', 9, unsigned int)
/** Start a DMA transfer */
#define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)

[ grab the code from here: http://repo.or.cz/w/tsi148vmebridge.git .
  Please note there's some backwards-compatible crap for old user-space
  LynxOS code, called CES. Ignore it. ]

By the way, please try to be consistent with naming, e.g. I see window,
then see "image", and I have to think hard to see whether they mean
the same or not.. err, do they?

E.

(I'll leave this here because of added Cc)
Greg K-H wrote:
> From: Martyn Welch <martyn.welch@gefanuc.com>
> 
> Adds a VME userspace access driver
> 
> Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> ---
>  drivers/staging/vme/Kconfig            |    4 
>  drivers/staging/vme/Makefile           |    2 
>  drivers/staging/vme/devices/Kconfig    |    8 
>  drivers/staging/vme/devices/Makefile   |    5 
>  drivers/staging/vme/devices/vme_user.c |  771 +++++++++++++++++++++++++++++++++
>  drivers/staging/vme/devices/vme_user.h |   43 +
>  6 files changed, 830 insertions(+), 3 deletions(-)
> 
> --- /dev/null
> +++ b/drivers/staging/vme/devices/Kconfig
> @@ -0,0 +1,8 @@
> +comment "VME Device Drivers"
> +
> +config VME_USER
> +	tristate "VME user space access driver"
> +	help
> +	  If you say Y here you want to be able to access a limited number of
> +	  VME windows in a manner at least semi-compatible with the interface
> +	  provided with the original driver at http://vmelinux.org/.
> --- /dev/null
> +++ b/drivers/staging/vme/devices/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for the VME device drivers.
> +#
> +
> +obj-$(CONFIG_VME_USER)		+= vme_user.o
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_user.c
> @@ -0,0 +1,771 @@
> +/*
> + * VMEbus User access driver
> + *
> + * Author: Martyn Welch <martyn.welch@gefanuc.com>
> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
> + *
> + * Based on work by:
> + *   Tom Armistead and Ajit Prem
> + *     Copyright 2004 Motorola Inc.
> + *
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/cdev.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/ioctl.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/pagemap.h>
> +#include <linux/pci.h>
> +#include <linux/semaphore.h>
> +#include <linux/spinlock.h>
> +#include <linux/syscalls.h>
> +#include <linux/types.h>
> +#include <linux/version.h>
> +
> +#include <asm/io.h>
> +#include <asm/uaccess.h>
> +
> +#include "../vme.h"
> +#include "vme_user.h"
> +
> +/* Currently Documentation/devices.txt defines the following for VME:
> + *
> + * 221 char	VME bus
> + * 		  0 = /dev/bus/vme/m0		First master image
> + * 		  1 = /dev/bus/vme/m1		Second master image
> + * 		  2 = /dev/bus/vme/m2		Third master image
> + * 		  3 = /dev/bus/vme/m3		Fourth master image
> + * 		  4 = /dev/bus/vme/s0		First slave image
> + * 		  5 = /dev/bus/vme/s1		Second slave image
> + * 		  6 = /dev/bus/vme/s2		Third slave image
> + * 		  7 = /dev/bus/vme/s3		Fourth slave image
> + * 		  8 = /dev/bus/vme/ctl		Control
> + *
> + * 		It is expected that all VME bus drivers will use the
> + * 		same interface.  For interface documentation see
> + * 		http://www.vmelinux.org/.
> + *
> + * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
> + * even support the tsi148 chipset (which has 8 master and 8 slave windows).
> + * We'll run with this or now as far as possible, however it probably makes
> + * sense to get rid of the old mappings and just do everything dynamically.
> + *
> + * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
> + * defined above and try to support at least some of the interface from
> + * http://www.vmelinux.org/ as an alternative drive can be written providing a
> + * saner interface later.
> + */
> +#define VME_MAJOR	221	/* VME Major Device Number */
> +#define VME_DEVS	9	/* Number of dev entries */
> +
> +#define MASTER_MINOR	0
> +#define MASTER_MAX	3
> +#define SLAVE_MINOR	4
> +#define SLAVE_MAX	7
> +#define CONTROL_MINOR	8
> +
> +#define PCI_BUF_SIZE  0x20000	/* Size of one slave image buffer */
> +
> +/*
> + * Structure to handle image related parameters.
> + */
> +typedef struct {
> +	void __iomem *kern_buf;	/* Buffer address in kernel space */
> +	dma_addr_t pci_buf;	/* Buffer address in PCI address space */
> +	unsigned long long size_buf;	/* Buffer size */
> +	struct semaphore sem;	/* Semaphore for locking image */
> +	struct device *device;	/* Sysfs device */
> +	struct vme_resource *resource;	/* VME resource */
> +	int users;		/* Number of current users */
> +} image_desc_t;
> +static image_desc_t image[VME_DEVS];
> +
> +typedef struct {
> +	unsigned long reads;
> +	unsigned long writes;
> +	unsigned long ioctls;
> +	unsigned long irqs;
> +	unsigned long berrs;
> +	unsigned long dmaErrors;
> +	unsigned long timeouts;
> +	unsigned long external;
> +} driver_stats_t;
> +static driver_stats_t statistics;
> +
> +struct cdev *vme_user_cdev;		/* Character device */
> +struct class *vme_user_sysfs_class;	/* Sysfs class */
> +struct device *vme_user_bridge;		/* Pointer to the bridge device */
> +
> +static char driver_name[] = "vme_user";
> +
> +static const int type[VME_DEVS] = {	MASTER_MINOR,	MASTER_MINOR,
> +					MASTER_MINOR,	MASTER_MINOR,
> +					SLAVE_MINOR,	SLAVE_MINOR,
> +					SLAVE_MINOR,	SLAVE_MINOR,
> +					CONTROL_MINOR
> +				};
> +
> +
> +static int vme_user_open(struct inode *, struct file *);
> +static int vme_user_release(struct inode *, struct file *);
> +static ssize_t vme_user_read(struct file *, char *, size_t, loff_t *);
> +static ssize_t vme_user_write(struct file *, const char *, size_t, loff_t *);
> +static loff_t vme_user_llseek(struct file *, loff_t, int);
> +static int vme_user_ioctl(struct inode *, struct file *, unsigned int,
> +	unsigned long);
> +
> +static int __init vme_user_probe(struct device *dev);
> +
> +static struct file_operations vme_user_fops = {
> +        .open = vme_user_open,
> +        .release = vme_user_release,
> +        .read = vme_user_read,
> +        .write = vme_user_write,
> +        .llseek = vme_user_llseek,
> +        .ioctl = vme_user_ioctl,
> +};
> +
> +
> +/*
> + * Reset all the statistic counters
> + */
> +static void reset_counters(void)
> +{
> +        statistics.reads = 0;
> +        statistics.writes = 0;
> +        statistics.ioctls = 0;
> +        statistics.irqs = 0;
> +        statistics.berrs = 0;
> +        statistics.dmaErrors = 0;
> +        statistics.timeouts = 0;
> +}
> +
> +void lmcall(int monitor)
> +{
> +	printk("Caught Location Monitor %d access\n", monitor);
> +}
> +
> +static void tests(void)
> +{
> +	struct vme_resource *dma_res;
> +	struct vme_dma_list *dma_list;
> +	struct vme_dma_attr *pattern_attr, *vme_attr;
> +
> +	int retval;
> +	unsigned int data;
> +
> +	printk("Running VME DMA test\n");
> +	dma_res = vme_request_dma(vme_user_bridge);
> +	dma_list = vme_new_dma_list(dma_res);
> +	pattern_attr = vme_dma_pattern_attribute(0x0,
> +		VME_DMA_PATTERN_WORD |
> +			VME_DMA_PATTERN_INCREMENT);
> +	vme_attr = vme_dma_vme_attribute(0x10000, VME_A32,
> +		VME_SCT, VME_D32);
> +	retval = vme_dma_list_add(dma_list, pattern_attr,
> +		vme_attr, 0x10000);
> +#if 0
> +	vme_dma_free_attribute(vme_attr);
> +	vme_attr = vme_dma_vme_attribute(0x20000, VME_A32,
> +		VME_SCT, VME_D32);
> +	retval = vme_dma_list_add(dma_list, pattern_attr,
> +		vme_attr, 0x10000);
> +#endif
> +	retval = vme_dma_list_exec(dma_list);
> +	vme_dma_free_attribute(pattern_attr);
> +	vme_dma_free_attribute(vme_attr);
> +	vme_dma_list_free(dma_list);
> +#if 0
> +	printk("Generating a VME interrupt\n");
> +	vme_generate_irq(dma_res, 0x3, 0xaa);
> +	printk("Interrupt returned\n");
> +#endif
> +	vme_dma_free(dma_res);
> +
> +	/* Attempt RMW */
> +	data = vme_master_rmw(image[0].resource, 0x80000000, 0x00000000,
> +		0x80000000, 0);
> +	printk("RMW returned 0x%8.8x\n", data);
> +
> +
> +	/* Location Monitor */
> +	printk("vme_lm_set:%d\n", vme_lm_set(vme_user_bridge, 0x60000, VME_A32, VME_SCT | VME_USER | VME_DATA));
> +	printk("vme_lm_attach:%d\n", vme_lm_attach(vme_user_bridge, 0, lmcall));
> +
> +	printk("Board in VME slot:%d\n", vme_slot_get(vme_user_bridge));
> +}
> +
> +static int vme_user_open(struct inode *inode, struct file *file)
> +{
> +	int err;
> +	unsigned int minor = MINOR(inode->i_rdev);
> +
> +	down(&image[minor].sem);
> +	/* Only allow device to be opened if a resource is allocated */
> +	if (image[minor].resource == NULL) {
> +		printk(KERN_ERR "No resources allocated for device\n");
> +		err = -EINVAL;
> +		goto err_res;
> +	}
> +
> +	/* Increment user count */
> +	image[minor].users++;
> +
> +	up(&image[minor].sem);
> +
> +	return 0;
> +
> +err_res:
> +	up(&image[minor].sem);
> +
> +	return err;
> +}
> +
> +static int vme_user_release(struct inode *inode, struct file *file)
> +{
> +	unsigned int minor = MINOR(inode->i_rdev);
> +
> +	down(&image[minor].sem);
> +
> +	/* Decrement user count */
> +	image[minor].users--;
> +
> +	up(&image[minor].sem);
> +
> +	return 0;
> +}
> +
> +/*
> + * We are going ot alloc a page during init per window for small transfers.
> + * Small transfers will go VME -> buffer -> user space. Larger (more than a
> + * page) transfers will lock the user space buffer into memory and then
> + * transfer the data directly into the user space buffers.
> + */
> +static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
> +	loff_t *ppos)
> +{
> +	ssize_t retval;
> +	ssize_t copied = 0;
> +
> +	if (count <= image[minor].size_buf) {
> +		/* We copy to kernel buffer */
> +		copied = vme_master_read(image[minor].resource,
> +			image[minor].kern_buf, count, *ppos);
> +		if (copied < 0) {
> +			return (int)copied;
> +		}
> +
> +		retval = __copy_to_user(buf, image[minor].kern_buf,
> +			(unsigned long)copied);
> +		if (retval != 0) {
> +			copied = (copied - retval);
> +			printk("User copy failed\n");
> +			return -EINVAL;
> +		}
> +
> +	} else {
> +		/* XXX Need to write this */
> +		printk("Currently don't support large transfers\n");
> +		/* Map in pages from userspace */
> +
> +		/* Call vme_master_read to do the transfer */
> +		return -EINVAL;
> +	}
> +
> +	return copied;
> +}
> +
> +/*
> + * We are going ot alloc a page during init per window for small transfers.
> + * Small transfers will go user space -> buffer -> VME. Larger (more than a
> + * page) transfers will lock the user space buffer into memory and then
> + * transfer the data directly from the user space buffers out to VME.
> + */
> +static ssize_t resource_from_user(unsigned int minor, const char *buf,
> +	size_t count, loff_t *ppos)
> +{
> +	ssize_t retval;
> +	ssize_t copied = 0;
> +
> +	if (count <= image[minor].size_buf) {
> +		retval = __copy_from_user(image[minor].kern_buf, buf,
> +			(unsigned long)count);
> +		if (retval != 0)
> +			copied = (copied - retval);
> +		else
> +			copied = count;
> +
> +		copied = vme_master_write(image[minor].resource,
> +			image[minor].kern_buf, copied, *ppos);
> +	} else {
> +		/* XXX Need to write this */
> +		printk("Currently don't support large transfers\n");
> +		/* Map in pages from userspace */
> +
> +		/* Call vme_master_write to do the transfer */
> +		return -EINVAL;
> +	}
> +
> +	return copied;
> +}
> +
> +static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
> +	size_t count, loff_t *ppos)
> +{
> +	void __iomem *image_ptr;
> +	ssize_t retval;
> +
> +	image_ptr = image[minor].kern_buf + *ppos;
> +
> +	retval = __copy_to_user(buf, image_ptr, (unsigned long)count);
> +	if (retval != 0) {
> +		retval = (count - retval);
> +		printk(KERN_WARNING "Partial copy to userspace\n");
> +	} else
> +		retval = count;
> +
> +	/* Return number of bytes successfully read */
> +	return retval;
> +}
> +
> +static ssize_t buffer_from_user(unsigned int minor, const char *buf,
> +	size_t count, loff_t *ppos)
> +{
> +	void __iomem *image_ptr;
> +	size_t retval;
> +
> +	image_ptr = image[minor].kern_buf + *ppos;
> +
> +	retval = __copy_from_user(image_ptr, buf, (unsigned long)count);
> +	if (retval != 0) {
> +		retval = (count - retval);
> +		printk(KERN_WARNING "Partial copy to userspace\n");
> +	} else
> +		retval = count;
> +
> +	/* Return number of bytes successfully read */
> +	return retval;
> +}
> +
> +static ssize_t vme_user_read(struct file *file, char *buf, size_t count,
> +			loff_t * ppos)
> +{
> +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
> +	ssize_t retval;
> +	size_t image_size;
> +	size_t okcount;
> +
> +	down(&image[minor].sem);
> +
> +	/* XXX Do we *really* want this helper - we can use vme_*_get ? */
> +	image_size = vme_get_size(image[minor].resource);
> +
> +	/* Ensure we are starting at a valid location */
> +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
> +		up(&image[minor].sem);
> +		return 0;
> +	}
> +
> +	/* Ensure not reading past end of the image */
> +	if (*ppos + count > image_size)
> +		okcount = image_size - *ppos;
> +	else
> +		okcount = count;
> +
> +	switch (type[minor]){
> +	case MASTER_MINOR:
> +		retval = resource_to_user(minor, buf, okcount, ppos);
> +		break;
> +	case SLAVE_MINOR:
> +		retval = buffer_to_user(minor, buf, okcount, ppos);
> +		break;
> +	default:
> +		retval = -EINVAL;
> +	}
> +
> +	up(&image[minor].sem);
> +
> +	if (retval > 0)
> +		*ppos += retval;
> +
> +	return retval;
> +}
> +
> +static ssize_t vme_user_write(struct file *file, const char *buf, size_t count,
> +			 loff_t *ppos)
> +{
> +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
> +	ssize_t retval;
> +	size_t image_size;
> +	size_t okcount;
> +
> +	down(&image[minor].sem);
> +
> +	image_size = vme_get_size(image[minor].resource);
> +
> +	/* Ensure we are starting at a valid location */
> +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
> +		up(&image[minor].sem);
> +		return 0;
> +	}
> +
> +	/* Ensure not reading past end of the image */
> +	if (*ppos + count > image_size)
> +		okcount = image_size - *ppos;
> +	else
> +		okcount = count;
> +
> +	switch (type[minor]){
> +	case MASTER_MINOR:
> +		retval = resource_from_user(minor, buf, okcount, ppos);
> +		break;
> +	case SLAVE_MINOR:
> +		retval = buffer_from_user(minor, buf, okcount, ppos);
> +		break;
> +	default:
> +		retval = -EINVAL;
> +	}
> +
> +	up(&image[minor].sem);
> +
> +	if (retval > 0)
> +		*ppos += retval;
> +
> +	return retval;
> +}
> +
> +static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
> +{
> +	printk(KERN_ERR "Llseek currently incomplete\n");
> +	return -EINVAL;
> +}
> +
> +static int vme_user_ioctl(struct inode *inode, struct file *file,
> +	unsigned int cmd, unsigned long arg)
> +{
> +	unsigned int minor = MINOR(inode->i_rdev);
> +#if 0
> +	int ret_val;
> +#endif
> +	unsigned long copyRet;
> +	vme_slave_t slave;
> +
> +	statistics.ioctls++;
> +	switch (type[minor]) {
> +	case CONTROL_MINOR:
> +		break;
> +	case MASTER_MINOR:
> +		break;
> +	case SLAVE_MINOR:
> +		switch (cmd) {
> +		case VME_SET_SLAVE:
> +
> +			copyRet = copy_from_user(&slave, (char *)arg,
> +				sizeof(slave));
> +			if (copyRet != 0) {
> +				printk(KERN_WARNING "Partial copy from "
> +					"userspace\n");
> +				return -EFAULT;
> +			}
> +
> +			return vme_slave_set(image[minor].resource,
> +				slave.enable, slave.vme_addr, slave.size,
> +				image[minor].pci_buf, slave.aspace,
> +				slave.cycle);
> +
> +			break;
> +#if 0
> +		case VME_GET_SLAVE:
> +			vme_slave_t slave;
> +
> +			ret_val = vme_slave_get(minor, &iRegs);
> +
> +			copyRet = copy_to_user((char *)arg, &slave,
> +				sizeof(slave));
> +			if (copyRet != 0) {
> +				printk(KERN_WARNING "Partial copy to "
> +					"userspace\n");
> +				return -EFAULT;
> +			}
> +
> +			return ret_val;
> +			break;
> +#endif
> +		}
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +
> +/*
> + * Unallocate a previously allocated buffer
> + */
> +static void buf_unalloc (int num)
> +{
> +	if (image[num].kern_buf) {
> +#ifdef VME_DEBUG
> +		printk(KERN_DEBUG "UniverseII:Releasing buffer at %p\n",
> +			image[num].pci_buf);
> +#endif
> +
> +		vme_free_consistent(image[num].resource, image[num].size_buf,
> +			image[num].kern_buf, image[num].pci_buf);
> +
> +		image[num].kern_buf = NULL;
> +		image[num].pci_buf = 0;
> +		image[num].size_buf = 0;
> +
> +#ifdef VME_DEBUG
> +	} else {
> +		printk(KERN_DEBUG "UniverseII: Buffer not allocated\n");
> +#endif
> +	}
> +}
> +
> +static struct vme_driver vme_user_driver = {
> +        .name = driver_name,
> +        .probe = vme_user_probe,
> +};
> +
> +
> +/*
> + * In this simple access driver, the old behaviour is being preserved as much
> + * as practical. We will therefore reserve the buffers and request the images
> + * here so that we don't have to do it later.
> + */
> +static int __init vme_bridge_init(void)
> +{
> +	int retval;
> +	printk(KERN_INFO "VME User Space Access Driver\n");
> +	printk("vme_user_driver:%p\n", &vme_user_driver);
> +	retval = vme_register_driver(&vme_user_driver);
> +	printk("vme_register_driver returned %d\n", retval);
> +	return retval;
> +}
> +
> +/*
> + * This structure gets passed a device, this should be the device created at
> + * registration.
> + */
> +static int __init vme_user_probe(struct device *dev)
> +{
> +	int i, err;
> +	char name[8];
> +
> +	printk("Running vme_user_probe()\n");
> +
> +	/* Pointer to the bridge device */
> +	vme_user_bridge = dev;
> +
> +	/* Initialise descriptors */
> +	for (i = 0; i < VME_DEVS; i++) {
> +		image[i].kern_buf = NULL;
> +		image[i].pci_buf = 0;
> +		init_MUTEX(&(image[i].sem));
> +		image[i].device = NULL;
> +		image[i].resource = NULL;
> +		image[i].users = 0;
> +	}
> +
> +	/* Initialise statistics counters */
> +	reset_counters();
> +
> +	/* Assign major and minor numbers for the driver */
> +	err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS,
> +		driver_name);
> +	if (err) {
> +		printk(KERN_WARNING "%s: Error getting Major Number %d for "
> +		"driver.\n", driver_name, VME_MAJOR);
> +		goto err_region;
> +	}
> +
> +	/* Register the driver as a char device */
> +	vme_user_cdev = cdev_alloc();
> +	vme_user_cdev->ops = &vme_user_fops;
> +	vme_user_cdev->owner = THIS_MODULE;
> +	err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
> +	if (err) {
> +		printk(KERN_WARNING "%s: cdev_all failed\n", driver_name);
> +		goto err_char;
> +	}
> +
> +	/* Request slave resources and allocate buffers (128kB wide) */
> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> +		/* XXX Need to properly request attributes */
> +		image[i].resource = vme_slave_request(vme_user_bridge,
> +			VME_A16, VME_SCT);
> +		if (image[i].resource == NULL) {
> +			printk(KERN_WARNING "Unable to allocate slave "
> +				"resource\n");
> +			goto err_buf;
> +		}
> +		image[i].size_buf = PCI_BUF_SIZE;
> +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
> +			image[i].size_buf, &(image[i].pci_buf));
> +		if (image[i].kern_buf == NULL) {
> +			printk(KERN_WARNING "Unable to allocate memory for "
> +				"buffer\n");
> +			image[i].pci_buf = 0;
> +			vme_slave_free(image[i].resource);
> +			err = -ENOMEM;
> +			goto err_buf;
> +		}
> +	}
> +
> +	/*
> +	 * Request master resources allocate page sized buffers for small
> +	 * reads and writes
> +	 */
> +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
> +		/* XXX Need to properly request attributes */
> +		image[i].resource = vme_master_request(vme_user_bridge,
> +			VME_A32, VME_SCT, VME_D32);
> +		if (image[i].resource == NULL) {
> +			printk(KERN_WARNING "Unable to allocate master "
> +				"resource\n");
> +			goto err_buf;
> +		}
> +		image[i].size_buf = PAGE_SIZE;
> +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
> +			image[i].size_buf, &(image[i].pci_buf));
> +		if (image[i].kern_buf == NULL) {
> +			printk(KERN_WARNING "Unable to allocate memory for "
> +				"buffer\n");
> +			image[i].pci_buf = 0;
> +			vme_master_free(image[i].resource);
> +			err = -ENOMEM;
> +			goto err_buf;
> +		}
> +	}
> +
> +	/* Setup some debug windows */
> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> +		err = vme_slave_set(image[i].resource, 1, 0x4000*(i-4),
> +			0x4000, image[i].pci_buf, VME_A16,
> +			VME_SCT | VME_SUPER | VME_USER | VME_PROG | VME_DATA);
> +		if (err != 0) {
> +			printk(KERN_WARNING "Failed to configure window\n");
> +			goto err_buf;
> +		}
> +	}
> +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
> +		err = vme_master_set(image[i].resource, 1,
> +			(0x10000 + (0x10000*i)), 0x10000,
> +			VME_A32, VME_SCT | VME_USER | VME_DATA, VME_D32);
> +		if (err != 0) {
> +			printk(KERN_WARNING "Failed to configure window\n");
> +			goto err_buf;
> +		}
> +	}
> +
> +	/* Create sysfs entries - on udev systems this creates the dev files */
> +	vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
> +	if (IS_ERR(vme_user_sysfs_class)) {
> +		printk(KERN_ERR "Error creating vme_user class.\n");
> +		err = PTR_ERR(vme_user_sysfs_class);
> +		goto err_class;
> +	}
> +
> +	/* Add sysfs Entries */
> +	for (i=0; i<VME_DEVS; i++) {
> +		switch (type[i]) {
> +		case MASTER_MINOR:
> +			sprintf(name,"bus/vme/m%%d");
> +			break;
> +		case CONTROL_MINOR:
> +			sprintf(name,"bus/vme/ctl");
> +			break;
> +		case SLAVE_MINOR:
> +			sprintf(name,"bus/vme/s%%d");
> +			break;
> +		default:
> +			err = -EINVAL;
> +			goto err_sysfs;
> +			break;
> +		}
> +
> +		image[i].device =
> +			device_create(vme_user_sysfs_class, NULL,
> +				MKDEV(VME_MAJOR, i), NULL, name,
> +				(type[i] == SLAVE_MINOR)? i - (MASTER_MAX + 1) : i);
> +		if (IS_ERR(image[i].device)) {
> +			printk("%s: Error creating sysfs device\n",
> +				driver_name);
> +			err = PTR_ERR(image[i].device);
> +			goto err_sysfs;
> +		}
> +	}
> +
> +	/* XXX Run tests */
> +	/*
> +	tests();
> +	*/
> +
> +	return 0;
> +
> +	/* Ensure counter set correcty to destroy all sysfs devices */
> +	i = VME_DEVS;
> +err_sysfs:
> +	while (i > 0){
> +		i--;
> +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
> +	}
> +	class_destroy(vme_user_sysfs_class);
> +
> +	/* Ensure counter set correcty to unalloc all slave buffers */
> +	i = SLAVE_MAX + 1;
> +err_buf:
> +	while (i > SLAVE_MINOR){
> +		i--;
> +		vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
> +		vme_slave_free(image[i].resource);
> +		buf_unalloc(i);
> +	}
> +err_class:
> +	cdev_del(vme_user_cdev);
> +err_char:
> +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
> +err_region:
> +	return err;
> +}
> +
> +static void __exit vme_bridge_exit(void)
> +{
> +	int i;
> +
> +	/* Remove sysfs Entries */
> +	for(i=0; i<VME_DEVS; i++) {
> +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
> +	}
> +	class_destroy(vme_user_sysfs_class);
> +
> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> +		buf_unalloc(i);
> +	}
> +
> +	/* Unregister device driver */
> +	cdev_del(vme_user_cdev);
> +
> +	/* Unregiser the major and minor device numbers */
> +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
> +}
> +
> +MODULE_DESCRIPTION("VME User Space Access Driver");
> +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
> +MODULE_LICENSE("GPL");
> +
> +module_init(vme_bridge_init);
> +module_exit(vme_bridge_exit);
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_user.h
> @@ -0,0 +1,43 @@
> +
> +/*
> + * VMEbus Master Window Configuration Structure
> + */
> +typedef struct {
> +	char enable;			/* State of Window */
> +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
> +	unsigned long long size;	/* Window Size */
> +	vme_address_t aspace;		/* Address Space */
> +	vme_cycle_t cycle;		/* Cycle properties */
> +	vme_width_t dwidth;		/* Maximum Data Width */
> +#if 0
> +	char prefetchEnable;		/* Prefetch Read Enable State */
> +	int prefetchSize;		/* Prefetch Read Size (Cache Lines) */
> +	char wrPostEnable;		/* Write Post State */
> +#endif
> +} vme_master_t;
> +
> +
> +/*
> + * IOCTL Commands and structures
> + */
> +
> +/* Magic number for use in ioctls */
> +#define VME_IOC_MAGIC 0xAE
> +
> +
> +/* VMEbus Slave Window Configuration Structure */
> +typedef struct {
> +	char enable;			/* State of Window */
> +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
> +	unsigned long long size;	/* Window Size */
> +	vme_address_t aspace;		/* Address Space */
> +	vme_cycle_t cycle;		/* Cycle properties */
> +#if 0
> +	char wrPostEnable;		/* Write Post State */
> +	char rmwLock;			/* Lock PCI during RMW Cycles */
> +	char data64BitCapable;		/* non-VMEbus capable of 64-bit Data */
> +#endif
> +} vme_slave_t;
> +
> +#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 10, vme_slave_t)
> +
> --- a/drivers/staging/vme/Kconfig
> +++ b/drivers/staging/vme/Kconfig
> @@ -11,7 +11,7 @@ menuconfig VME
>  if VME
>  
>  #source "drivers/staging/vme/bridges/Kconfig"
> -#
> -#source "drivers/staging/vme/devices/Kconfig"
> +
> +source "drivers/staging/vme/devices/Kconfig"
>  
>  endif # VME
> --- a/drivers/staging/vme/Makefile
> +++ b/drivers/staging/vme/Makefile
> @@ -4,4 +4,4 @@
>  obj-$(CONFIG_VME)		+= vme.o
>  
>  #obj-y				+= bridges/
> -#obj-y				+= devices/
> +obj-y				+= devices/
> 
> 

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

* Re: [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver
  2009-08-03 21:01   ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Greg K-H
  2009-08-03 22:50     ` Jiri Slaby
@ 2009-08-09  0:09     ` Emilio G. Cota
  2009-08-11 14:59       ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridgedriver Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-09  0:09 UTC (permalink / raw)
  To: Greg K-H; +Cc: linux-kernel, devel, Martyn Welch, Sebastien Dugue

Greg, Martyn,

please have a look at our tsi148 driver, available at:
http://repo.or.cz/w/tsi148vmebridge.git
Whenever you encounter a reference to 'CES', ignore it,
it's a CERN thing for easing our lives porting drivers from
LynxOS.

I could post the driver to LKML for review if you want; I would
remove that CES crap before posting. I put this off because
I knew it would never get merged before there was some generic
vme glue supporting it. However now we're getting there so
it's probably a good time to get cracking with this task.

Martyn, a few comments about your driver, haven't had much
time to dig very deep.

- again, use mutexes instead of semaphores
- The DMA code looks rather messy; I mean stuff like this:
> +#if 0
> +     /* XXX Still todo */
> +     for (x = 0; x < 8; x++) {       /* vme block size */
> +             if ((32 << x) >= vmeDma->maxVmeBlockSize) {
> +                     break;
> +             }
> +     }
> +     if (x == 8)
> +             x = 7;
> +     dctlreg |= (x << 12);
> +
> +     for (x = 0; x < 8; x++) {       /* pci block size */
> +             if ((32 << x) >= vmeDma->maxPciBlockSize) {
> +                     break;
> +             }
> +     }
> +     if (x == 8)
> +             x = 7;
> +     dctlreg |= (x << 4);
> +
> +     if (vmeDma->vmeBackOffTimer) {
and the ifdefs 0..

- correct me if I'm wrong, but does the slave need to know the
  window number he wants for his device? See:
> +int tsi148_slave_get(struct vme_slave_resource *image, int *enabled,
> +     unsigned long long *vme_base, unsigned long long *size,
> +     dma_addr_t *pci_base, vme_address_t *aspace, vme_cycle_t *cycle)
> +{
> +     unsigned int i, granularity = 0, ctl = 0;
> +     unsigned int vme_base_low, vme_base_high;
> +     unsigned int vme_bound_low, vme_bound_high;
> +     unsigned int pci_offset_low, pci_offset_high;
> +     unsigned long long vme_bound, pci_offset;
> +
> +
> +     i = image->number;

If that's the case, this is totally broken; in fact, even if you
kept a list somewhere up on the generic vme layer keeping track
of the already used windows (images?) to avoid any clashes,
in practice you'd very soon run out of windows: 8 modules, 8
mappings, 8 windows. And you're dead.

In a crate we have up to 20 modules, each of them with at least
one address space to be mapped. To make this work we do the
following:

* The tsi148 has 1GB of address space available for mapping,
  which is further divided into 8 windows, with a unique address
  modifier per window.
* Say you have 15 modules that need to map 1MB each, all of them
  with the same address modifier (AM).
Then what the slave driver requests is *not* a window, is what we
call a 'vme mapping', which means "do whatever you want but give
me a kernel address that points at this VME address, mapping with
this size and AM".

Our tsi148 driver notices then that there's already a window for
that AM, so it just appends the mapping to the existing window.
So at the end of the day only one window is used for all those
mappings, which are nicely piled together.

Please clarify me how this is handled in your driver. The
bottom line is to ensure that
a) Many different modules can peacefully co-exist on a crate
b) Drivers should know nothing about what else is on your crate

E.

(I keep this here for the added Cc)
Greg K-H wrote:
> From: Martyn Welch <martyn.welch@gefanuc.com>
> 
> Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> ---
>  drivers/staging/vme/bridges/Kconfig      |    6 
>  drivers/staging/vme/bridges/Makefile     |    1 
>  drivers/staging/vme/bridges/vme_tsi148.c | 2912 +++++++++++++++++++++++++++++++
>  drivers/staging/vme/bridges/vme_tsi148.h | 1387 ++++++++++++++
>  4 files changed, 4306 insertions(+)
> 
> --- a/drivers/staging/vme/bridges/Kconfig
> +++ b/drivers/staging/vme/bridges/Kconfig
> @@ -6,3 +6,9 @@ config VME_CA91CX42
>  	help
>  	 If you say Y here you get support for the Tundra CA91C042 (Universe I)
>  	 and CA91C142 (Universe II) VME bridge chips.
> +
> +config VME_TSI148
> +	tristate "Tempe"
> +	help
> +	 If you say Y here you get support for the Tundra TSI148 VME bridge
> +	 chip.
> --- a/drivers/staging/vme/bridges/Makefile
> +++ b/drivers/staging/vme/bridges/Makefile
> @@ -1 +1,2 @@
>  obj-$(CONFIG_VME_CA91CX42)	+= vme_ca91cx42.o
> +obj-$(CONFIG_VME_TSI148)	+= vme_tsi148.o
> --- /dev/null
> +++ b/drivers/staging/vme/bridges/vme_tsi148.c
> @@ -0,0 +1,2912 @@
> +/*
> + * Support for the Tundra TSI148 VME-PCI Bridge Chip
> + *
> + * Author: Martyn Welch <martyn.welch@gefanuc.com>
> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
> + *
> + * Based on work by Tom Armistead and Ajit Prem
> + * Copyright 2004 Motorola Inc.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/mm.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/proc_fs.h>
> +#include <linux/pci.h>
> +#include <linux/poll.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <asm/time.h>
> +#include <asm/io.h>
> +#include <asm/uaccess.h>
> +
> +#include "../vme.h"
> +#include "../vme_bridge.h"
> +#include "vme_tsi148.h"
> +
> +static int __init tsi148_init(void);
> +static int tsi148_probe(struct pci_dev *, const struct pci_device_id *);
> +static void tsi148_remove(struct pci_dev *);
> +static void __exit tsi148_exit(void);
> +
> +
> +int tsi148_slave_set(struct vme_slave_resource *, int, unsigned long long,
> +	unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
> +int tsi148_slave_get(struct vme_slave_resource *, int *, unsigned long long *,
> +	unsigned long long *, dma_addr_t *, vme_address_t *, vme_cycle_t *);
> +
> +int tsi148_master_get(struct vme_master_resource *, int *, unsigned long long *,
> +        unsigned long long *, vme_address_t *, vme_cycle_t *, vme_width_t *);
> +int tsi148_master_set(struct vme_master_resource *, int, unsigned long long,
> +	unsigned long long, vme_address_t, vme_cycle_t,	vme_width_t);
> +ssize_t tsi148_master_read(struct vme_master_resource *, void *, size_t,
> +	loff_t);
> +ssize_t tsi148_master_write(struct vme_master_resource *, void *, size_t,
> +	loff_t);
> +unsigned int tsi148_master_rmw(struct vme_master_resource *, unsigned int,
> +	unsigned int, unsigned int, loff_t);
> +int tsi148_dma_list_add (struct vme_dma_list *, struct vme_dma_attr *,
> +	struct vme_dma_attr *, size_t);
> +int tsi148_dma_list_exec(struct vme_dma_list *);
> +int tsi148_dma_list_empty(struct vme_dma_list *);
> +int tsi148_generate_irq(int, int);
> +int tsi148_lm_set(unsigned long long, vme_address_t, vme_cycle_t);
> +int tsi148_lm_get(unsigned long long *, vme_address_t *, vme_cycle_t *);
> +int tsi148_lm_attach(int, void (*callback)(int));
> +int tsi148_lm_detach(int);
> +int tsi148_slot_get(void);
> +
> +/* Modue parameter */
> +int err_chk = 0;
> +
> +/* XXX These should all be in a per device structure */
> +struct vme_bridge *tsi148_bridge;
> +wait_queue_head_t dma_queue[2];
> +wait_queue_head_t iack_queue;
> +void (*lm_callback[4])(int);	/* Called in interrupt handler, be careful! */
> +void *crcsr_kernel;
> +dma_addr_t crcsr_bus;
> +struct vme_master_resource *flush_image;
> +struct semaphore vme_rmw;	/* Only one RMW cycle at a time */
> +struct semaphore vme_int;	/*
> +				 * Only one VME interrupt can be
> +				 * generated at a time, provide locking
> +				 */
> +struct semaphore vme_irq;	/* Locking for VME irq callback configuration */
> +struct semaphore vme_lm;	/* Locking for location monitor operations */
> +
> +
> +static char driver_name[] = "vme_tsi148";
> +
> +static struct pci_device_id tsi148_ids[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) },
> +	{ },
> +};
> +
> +static struct pci_driver tsi148_driver = {
> +	.name = driver_name,
> +	.id_table = tsi148_ids,
> +	.probe = tsi148_probe,
> +	.remove = tsi148_remove,
> +};
> +
> +static void reg_join(unsigned int high, unsigned int low,
> +	unsigned long long *variable)
> +{
> +	*variable = (unsigned long long)high << 32;
> +	*variable |= (unsigned long long)low;
> +}
> +
> +static void reg_split(unsigned long long variable, unsigned int *high,
> +	unsigned int *low)
> +{
> +	*low = (unsigned int)variable & 0xFFFFFFFF;
> +	*high = (unsigned int)(variable >> 32);
> +}
> +
> +/*
> + * Wakes up DMA queue.
> + */
> +static u32 tsi148_DMA_irqhandler(int channel_mask)
> +{
> +	u32 serviced = 0;
> +
> +	if (channel_mask & TSI148_LCSR_INTS_DMA0S) {
> +		wake_up(&dma_queue[0]);
> +		serviced |= TSI148_LCSR_INTC_DMA0C;
> +	}
> +	if (channel_mask & TSI148_LCSR_INTS_DMA1S) {
> +		wake_up(&dma_queue[1]);
> +		serviced |= TSI148_LCSR_INTC_DMA1C;
> +	}
> +
> +	return serviced;
> +}
> +
> +/*
> + * Wake up location monitor queue
> + */
> +static u32 tsi148_LM_irqhandler(u32 stat)
> +{
> +	int i;
> +	u32 serviced = 0;
> +
> +	for (i = 0; i < 4; i++) {
> +		if(stat & TSI148_LCSR_INTS_LMS[i]) {
> +			/* We only enable interrupts if the callback is set */
> +			lm_callback[i](i);
> +			serviced |= TSI148_LCSR_INTC_LMC[i];
> +		}
> +	}
> +
> +	return serviced;
> +}
> +
> +/*
> + * Wake up mail box queue.
> + *
> + * XXX This functionality is not exposed up though API.
> + */
> +static u32 tsi148_MB_irqhandler(u32 stat)
> +{
> +	int i;
> +	u32 val;
> +	u32 serviced = 0;
> +
> +	for (i = 0; i < 4; i++) {
> +		if(stat & TSI148_LCSR_INTS_MBS[i]) {
> +			val = ioread32be(tsi148_bridge->base +
> +				TSI148_GCSR_MBOX[i]);
> +			printk("VME Mailbox %d received: 0x%x\n", i, val);
> +			serviced |= TSI148_LCSR_INTC_MBC[i];
> +		}
> +	}
> +
> +	return serviced;
> +}
> +
> +/*
> + * Display error & status message when PERR (PCI) exception interrupt occurs.
> + */
> +static u32 tsi148_PERR_irqhandler(void)
> +{
> +	printk(KERN_ERR
> +		"PCI Exception at address: 0x%08x:%08x, attributes: %08x\n",
> +		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAU),
> +		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAL),
> +		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPAT)
> +		);
> +	printk(KERN_ERR
> +		"PCI-X attribute reg: %08x, PCI-X split completion reg: %08x\n",
> +		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPXA),
> +		ioread32be(tsi148_bridge->base + TSI148_LCSR_EDPXS)
> +		);
> +
> +	iowrite32be(TSI148_LCSR_EDPAT_EDPCL,
> +		tsi148_bridge->base + TSI148_LCSR_EDPAT);
> +
> +	return TSI148_LCSR_INTC_PERRC;
> +}
> +
> +/*
> + * Save address and status when VME error interrupt occurs.
> + */
> +static u32 tsi148_VERR_irqhandler(void)
> +{
> +	unsigned int error_addr_high, error_addr_low;
> +	unsigned long long error_addr;
> +	u32 error_attrib;
> +	struct vme_bus_error *error;
> +
> +	error_addr_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAU);
> +	error_addr_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAL);
> +	error_attrib = ioread32be(tsi148_bridge->base + TSI148_LCSR_VEAT);
> +
> +	reg_join(error_addr_high, error_addr_low, &error_addr);
> +
> +	/* Check for exception register overflow (we have lost error data) */
> +	if(error_attrib & TSI148_LCSR_VEAT_VEOF) {
> +		printk(KERN_ERR "VME Bus Exception Overflow Occurred\n");
> +	}
> +
> +	error = (struct vme_bus_error *)kmalloc(sizeof (struct vme_bus_error),
> +		GFP_ATOMIC);
> +	if (error) {
> +		error->address = error_addr;
> +		error->attributes = error_attrib;
> +		list_add_tail(&(error->list), &(tsi148_bridge->vme_errors));
> +	} else {
> +		printk(KERN_ERR
> +			"Unable to alloc memory for VMEbus Error reporting\n");
> +		printk(KERN_ERR
> +			"VME Bus Error at address: 0x%llx, attributes: %08x\n",
> +			error_addr, error_attrib);
> +	}
> +
> +	/* Clear Status */
> +	iowrite32be(TSI148_LCSR_VEAT_VESCL,
> +		tsi148_bridge->base + TSI148_LCSR_VEAT);
> +
> +	return TSI148_LCSR_INTC_VERRC;
> +}
> +
> +/*
> + * Wake up IACK queue.
> + */
> +static u32 tsi148_IACK_irqhandler(void)
> +{
> +	printk("tsi148_IACK_irqhandler\n");
> +	wake_up(&iack_queue);
> +
> +	return TSI148_LCSR_INTC_IACKC;
> +}
> +
> +/*
> + * Calling VME bus interrupt callback if provided.
> + */
> +static u32 tsi148_VIRQ_irqhandler(u32 stat)
> +{
> +	int vec, i, serviced = 0;
> +	void (*call)(int, int, void *);
> +	void *priv_data;
> +
> +	for (i = 7; i > 0; i--) {
> +		if (stat & (1 << i)) {
> +			/*
> +			 * 	Note:   Even though the registers are defined
> +			 * 	as 32-bits in the spec, we only want to issue
> +			 * 	8-bit IACK cycles on the bus, read from offset
> +			 * 	3.
> +			 */
> +			vec = ioread8(tsi148_bridge->base +
> +				TSI148_LCSR_VIACK[i] + 3);
> +
> +			call = tsi148_bridge->irq[i - 1].callback[vec].func;
> +			priv_data =
> +				tsi148_bridge->irq[i-1].callback[vec].priv_data;
> +
> +			if (call != NULL)
> +				call(i, vec, priv_data);
> +			else
> +				printk("Spurilous VME interrupt, level:%x, "
> +					"vector:%x\n", i, vec);
> +
> +			serviced |= (1 << i);
> +		}
> +	}
> +
> +	return serviced;
> +}
> +
> +/*
> + * Top level interrupt handler.  Clears appropriate interrupt status bits and
> + * then calls appropriate sub handler(s).
> + */
> +static irqreturn_t tsi148_irqhandler(int irq, void *dev_id)
> +{
> +	u32 stat, enable, serviced = 0;
> +
> +	/* Determine which interrupts are unmasked and set */
> +	enable = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	stat = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTS);
> +
> +	/* Only look at unmasked interrupts */
> +	stat &= enable;
> +
> +	if (unlikely(!stat)) {
> +		return IRQ_NONE;
> +	}
> +
> +	/* Call subhandlers as appropriate */
> +	/* DMA irqs */
> +	if (stat & (TSI148_LCSR_INTS_DMA1S | TSI148_LCSR_INTS_DMA0S))
> +		serviced |= tsi148_DMA_irqhandler(stat);
> +
> +	/* Location monitor irqs */
> +	if (stat & (TSI148_LCSR_INTS_LM3S | TSI148_LCSR_INTS_LM2S |
> +			TSI148_LCSR_INTS_LM1S | TSI148_LCSR_INTS_LM0S))
> +		serviced |= tsi148_LM_irqhandler(stat);
> +
> +	/* Mail box irqs */
> +	if (stat & (TSI148_LCSR_INTS_MB3S | TSI148_LCSR_INTS_MB2S |
> +			TSI148_LCSR_INTS_MB1S | TSI148_LCSR_INTS_MB0S))
> +		serviced |= tsi148_MB_irqhandler(stat);
> +
> +	/* PCI bus error */
> +	if (stat & TSI148_LCSR_INTS_PERRS)
> +		serviced |= tsi148_PERR_irqhandler();
> +
> +	/* VME bus error */
> +	if (stat & TSI148_LCSR_INTS_VERRS)
> +		serviced |= tsi148_VERR_irqhandler();
> +
> +	/* IACK irq */
> +	if (stat & TSI148_LCSR_INTS_IACKS)
> +		serviced |= tsi148_IACK_irqhandler();
> +
> +	/* VME bus irqs */
> +	if (stat & (TSI148_LCSR_INTS_IRQ7S | TSI148_LCSR_INTS_IRQ6S |
> +			TSI148_LCSR_INTS_IRQ5S | TSI148_LCSR_INTS_IRQ4S |
> +			TSI148_LCSR_INTS_IRQ3S | TSI148_LCSR_INTS_IRQ2S |
> +			TSI148_LCSR_INTS_IRQ1S))
> +		serviced |= tsi148_VIRQ_irqhandler(stat);
> +
> +	/* Clear serviced interrupts */
> +	iowrite32be(serviced, tsi148_bridge->base + TSI148_LCSR_INTC);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int tsi148_irq_init(struct vme_bridge *bridge)
> +{
> +	int result;
> +	unsigned int tmp;
> +	struct pci_dev *pdev;
> +
> +	/* Need pdev */
> +        pdev = container_of(bridge->parent, struct pci_dev, dev);
> +
> +	/* Initialise list for VME bus errors */
> +	INIT_LIST_HEAD(&(bridge->vme_errors));
> +
> +	result = request_irq(pdev->irq,
> +			     tsi148_irqhandler,
> +			     IRQF_SHARED,
> +			     driver_name, pdev);
> +	if (result) {
> +		dev_err(&pdev->dev, "Can't get assigned pci irq vector %02X\n",
> +			pdev->irq);
> +		return result;
> +	}
> +
> +	/* Enable and unmask interrupts */
> +	tmp = TSI148_LCSR_INTEO_DMA1EO | TSI148_LCSR_INTEO_DMA0EO |
> +		TSI148_LCSR_INTEO_MB3EO | TSI148_LCSR_INTEO_MB2EO |
> +		TSI148_LCSR_INTEO_MB1EO | TSI148_LCSR_INTEO_MB0EO |
> +		TSI148_LCSR_INTEO_PERREO | TSI148_LCSR_INTEO_VERREO |
> +		TSI148_LCSR_INTEO_IACKEO;
> +
> +	/* XXX This leaves the following interrupts masked.
> +	 * TSI148_LCSR_INTEO_VIEEO
> +	 * TSI148_LCSR_INTEO_SYSFLEO
> +	 * TSI148_LCSR_INTEO_ACFLEO
> +	 */
> +
> +	/* Don't enable Location Monitor interrupts here - they will be
> +	 * enabled when the location monitors are properly configured and
> +	 * a callback has been attached.
> +	 * TSI148_LCSR_INTEO_LM0EO
> +	 * TSI148_LCSR_INTEO_LM1EO
> +	 * TSI148_LCSR_INTEO_LM2EO
> +	 * TSI148_LCSR_INTEO_LM3EO
> +	 */
> +
> +	/* Don't enable VME interrupts until we add a handler, else the board
> +	 * will respond to it and we don't want that unless it knows how to
> +	 * properly deal with it.
> +	 * TSI148_LCSR_INTEO_IRQ7EO
> +	 * TSI148_LCSR_INTEO_IRQ6EO
> +	 * TSI148_LCSR_INTEO_IRQ5EO
> +	 * TSI148_LCSR_INTEO_IRQ4EO
> +	 * TSI148_LCSR_INTEO_IRQ3EO
> +	 * TSI148_LCSR_INTEO_IRQ2EO
> +	 * TSI148_LCSR_INTEO_IRQ1EO
> +	 */
> +
> +	iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO);
> +	iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN);
> +
> +	return 0;
> +}
> +
> +static void tsi148_irq_exit(struct pci_dev *pdev)
> +{
> +	/* Turn off interrupts */
> +	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +	/* Clear all interrupts */
> +	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTC);
> +
> +	/* Detach interrupt handler */
> +	free_irq(pdev->irq, pdev);
> +}
> +
> +/*
> + * Check to see if an IACk has been received, return true (1) or false (0).
> + */
> +int tsi148_iack_received(void)
> +{
> +	u32 tmp;
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR);
> +
> +	if (tmp & TSI148_LCSR_VICR_IRQS)
> +		return 0;
> +	else
> +		return 1;
> +}
> +
> +/*
> + * Set up an VME interrupt
> + */
> +int tsi148_request_irq(int level, int statid,
> +	void (*callback)(int level, int vector, void *priv_data),
> +        void *priv_data)
> +{
> +	u32 tmp;
> +
> +	/* Get semaphore */
> +	down(&(vme_irq));
> +
> +	if(tsi148_bridge->irq[level - 1].callback[statid].func) {
> +		up(&(vme_irq));
> +		printk("VME Interrupt already taken\n");
> +		return -EBUSY;
> +	}
> +
> +
> +	tsi148_bridge->irq[level - 1].count++;
> +	tsi148_bridge->irq[level - 1].callback[statid].priv_data = priv_data;
> +	tsi148_bridge->irq[level - 1].callback[statid].func = callback;
> +
> +	/* Enable IRQ level */
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	tmp |= TSI148_LCSR_INTEO_IRQEO[level - 1];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
> +	tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +	/* Release semaphore */
> +	up(&(vme_irq));
> +
> +	return 0;
> +}
> +
> +/*
> + * Free VME interrupt
> + */
> +void tsi148_free_irq(int level, int statid)
> +{
> +	u32 tmp;
> +
> +	/* Get semaphore */
> +	down(&(vme_irq));
> +
> +	tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
> +	tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
> +	tsi148_bridge->irq[level - 1].count--;
> +
> +	/* Disable IRQ level */
> +	if (tsi148_bridge->irq[level - 1].count == 0) {
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +		tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
> +		tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +	}
> +
> +	/* Release semaphore */
> +	up(&(vme_irq));
> +}
> +
> +/*
> + * Generate a VME bus interrupt at the requested level & vector. Wait for
> + * interrupt to be acked.
> + *
> + * Only one interrupt can be generated at a time - so add a semaphore.
> + */
> +int tsi148_generate_irq(int level, int statid)
> +{
> +	u32 tmp;
> +
> +	/* Get semaphore */
> +	down(&(vme_int));
> +
> +	/* Read VICR register */
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR);
> +
> +	/* Set Status/ID */
> +	tmp = (tmp & ~TSI148_LCSR_VICR_STID_M) |
> +		(statid & TSI148_LCSR_VICR_STID_M);
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VICR);
> +
> +	/* Assert VMEbus IRQ */
> +	tmp = tmp | TSI148_LCSR_VICR_IRQL[level];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VICR);
> +
> +	/* XXX Consider implementing a timeout? */
> +	wait_event_interruptible(iack_queue, tsi148_iack_received());
> +
> +	/* Release semaphore */
> +	up(&(vme_int));
> +
> +	return 0;
> +}
> +
> +/*
> + * Find the first error in this address range
> + */
> +static struct vme_bus_error *tsi148_find_error(vme_address_t aspace,
> +	unsigned long long address, size_t count)
> +{
> +	struct list_head *err_pos;
> +	struct vme_bus_error *vme_err, *valid = NULL;
> +	unsigned long long bound;
> +
> +	bound = address + count;
> +
> +	/*
> +	 * XXX We are currently not looking at the address space when parsing
> +	 *     for errors. This is because parsing the Address Modifier Codes
> +	 *     is going to be quite resource intensive to do properly. We
> +	 *     should be OK just looking at the addresses and this is certainly
> +	 *     much better than what we had before.
> +	 */
> +	err_pos = NULL;
> +	/* Iterate through errors */
> +	list_for_each(err_pos, &(tsi148_bridge->vme_errors)) {
> +		vme_err = list_entry(err_pos, struct vme_bus_error, list);
> +		if((vme_err->address >= address) && (vme_err->address < bound)){
> +			valid = vme_err;
> +			break;
> +		}
> +	}
> +
> +	return valid;
> +}
> +
> +/*
> + * Clear errors in the provided address range.
> + */
> +static void tsi148_clear_errors(vme_address_t aspace,
> +	unsigned long long address, size_t count)
> +{
> +	struct list_head *err_pos, *temp;
> +	struct vme_bus_error *vme_err;
> +	unsigned long long bound;
> +
> +	bound = address + count;
> +
> +	/*
> +	 * XXX We are currently not looking at the address space when parsing
> +	 *     for errors. This is because parsing the Address Modifier Codes
> +	 *     is going to be quite resource intensive to do properly. We
> +	 *     should be OK just looking at the addresses and this is certainly
> +	 *     much better than what we had before.
> +	 */
> +	err_pos = NULL;
> +	/* Iterate through errors */
> +	list_for_each_safe(err_pos, temp, &(tsi148_bridge->vme_errors)) {
> +		vme_err = list_entry(err_pos, struct vme_bus_error, list);
> +
> +		if((vme_err->address >= address) && (vme_err->address < bound)){
> +			list_del(err_pos);
> +			kfree(vme_err);
> +		}
> +	}
> +}
> +
> +/*
> + * Initialize a slave window with the requested attributes.
> + */
> +int tsi148_slave_set(struct vme_slave_resource *image, int enabled,
> +	unsigned long long vme_base, unsigned long long size,
> +	dma_addr_t pci_base, vme_address_t aspace, vme_cycle_t cycle)
> +{
> +	unsigned int i, addr = 0, granularity = 0;
> +	unsigned int temp_ctl = 0;
> +	unsigned int vme_base_low, vme_base_high;
> +	unsigned int vme_bound_low, vme_bound_high;
> +	unsigned int pci_offset_low, pci_offset_high;
> +	unsigned long long vme_bound, pci_offset;
> +
> +#if 0
> +        printk("Set slave image %d to:\n", image->number);
> + 	printk("\tEnabled: %s\n", (enabled == 1)? "yes" : "no");
> +	printk("\tVME Base:0x%llx\n", vme_base);
> +	printk("\tWindow Size:0x%llx\n", size);
> +	printk("\tPCI Base:0x%lx\n", (unsigned long)pci_base);
> +	printk("\tAddress Space:0x%x\n", aspace);
> +	printk("\tTransfer Cycle Properties:0x%x\n", cycle);
> +#endif
> +
> +	i = image->number;
> +
> +	switch (aspace) {
> +	case VME_A16:
> +		granularity = 0x10;
> +		addr |= TSI148_LCSR_ITAT_AS_A16;
> +		break;
> +	case VME_A24:
> +		granularity = 0x1000;
> +		addr |= TSI148_LCSR_ITAT_AS_A24;
> +		break;
> +	case VME_A32:
> +		granularity = 0x10000;
> +		addr |= TSI148_LCSR_ITAT_AS_A32;
> +		break;
> +	case VME_A64:
> +		granularity = 0x10000;
> +		addr |= TSI148_LCSR_ITAT_AS_A64;
> +		break;
> +	case VME_CRCSR:
> +	case VME_USER1:
> +	case VME_USER2:
> +	case VME_USER3:
> +	case VME_USER4:
> +	default:
> +		printk("Invalid address space\n");
> +		return -EINVAL;
> +		break;
> +	}
> +
> +	/* Convert 64-bit variables to 2x 32-bit variables */
> +	reg_split(vme_base, &vme_base_high, &vme_base_low);
> +
> +	/*
> +	 * Bound address is a valid address for the window, adjust
> +	 * accordingly
> +	 */
> +	vme_bound = vme_base + size - granularity;
> +	reg_split(vme_bound, &vme_bound_high, &vme_bound_low);
> +	pci_offset = (unsigned long long)pci_base - vme_base;
> +	reg_split(pci_offset, &pci_offset_high, &pci_offset_low);
> +
> +	if (vme_base_low & (granularity - 1)) {
> +		printk("Invalid VME base alignment\n");
> +		return -EINVAL;
> +	}
> +	if (vme_bound_low & (granularity - 1)) {
> +		printk("Invalid VME bound alignment\n");
> +		return -EINVAL;
> +	}
> +	if (pci_offset_low & (granularity - 1)) {
> +		printk("Invalid PCI Offset alignment\n");
> +		return -EINVAL;
> +	}
> +
> +#if 0
> +	printk("\tVME Bound:0x%llx\n", vme_bound);
> +	printk("\tPCI Offset:0x%llx\n", pci_offset);
> +#endif
> +
> +	/*  Disable while we are mucking around */
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITAT);
> +	temp_ctl &= ~TSI148_LCSR_ITAT_EN;
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITAT);
> +
> +	/* Setup mapping */
> +	iowrite32be(vme_base_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITSAU);
> +	iowrite32be(vme_base_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITSAL);
> +	iowrite32be(vme_bound_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITEAU);
> +	iowrite32be(vme_bound_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITEAL);
> +	iowrite32be(pci_offset_high, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITOFU);
> +	iowrite32be(pci_offset_low, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITOFL);
> +
> +/* XXX Prefetch stuff currently unsupported */
> +#if 0
> +
> +	for (x = 0; x < 4; x++) {
> +		if ((64 << x) >= vmeIn->prefetchSize) {
> +			break;
> +		}
> +	}
> +	if (x == 4)
> +		x--;
> +	temp_ctl |= (x << 16);
> +
> +	if (vmeIn->prefetchThreshold)
> +		if (vmeIn->prefetchThreshold)
> +			temp_ctl |= 0x40000;
> +#endif
> +
> +	/* Setup 2eSST speeds */
> +	temp_ctl &= ~TSI148_LCSR_ITAT_2eSSTM_M;
> +	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
> +	case VME_2eSST160:
> +		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_160;
> +		break;
> +	case VME_2eSST267:
> +		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_267;
> +		break;
> +	case VME_2eSST320:
> +		temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_320;
> +		break;
> +	}
> +
> +	/* Setup cycle types */
> +	temp_ctl &= ~(0x1F << 7);
> +	if (cycle & VME_BLT)
> +		temp_ctl |= TSI148_LCSR_ITAT_BLT;
> +	if (cycle & VME_MBLT)
> +		temp_ctl |= TSI148_LCSR_ITAT_MBLT;
> +	if (cycle & VME_2eVME)
> +		temp_ctl |= TSI148_LCSR_ITAT_2eVME;
> +	if (cycle & VME_2eSST)
> +		temp_ctl |= TSI148_LCSR_ITAT_2eSST;
> +	if (cycle & VME_2eSSTB)
> +		temp_ctl |= TSI148_LCSR_ITAT_2eSSTB;
> +
> +	/* Setup address space */
> +	temp_ctl &= ~TSI148_LCSR_ITAT_AS_M;
> +	temp_ctl |= addr;
> +
> +	temp_ctl &= ~0xF;
> +	if (cycle & VME_SUPER)
> +		temp_ctl |= TSI148_LCSR_ITAT_SUPR ;
> +	if (cycle & VME_USER)
> +		temp_ctl |= TSI148_LCSR_ITAT_NPRIV;
> +	if (cycle & VME_PROG)
> +		temp_ctl |= TSI148_LCSR_ITAT_PGM;
> +	if (cycle & VME_DATA)
> +		temp_ctl |= TSI148_LCSR_ITAT_DATA;
> +
> +	/* Write ctl reg without enable */
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITAT);
> +
> +	if (enabled)
> +		temp_ctl |= TSI148_LCSR_ITAT_EN;
> +
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITAT);
> +
> +	return 0;
> +}
> +
> +/*
> + * Get slave window configuration.
> + *
> + * XXX Prefetch currently unsupported.
> + */
> +int tsi148_slave_get(struct vme_slave_resource *image, int *enabled,
> +	unsigned long long *vme_base, unsigned long long *size,
> +	dma_addr_t *pci_base, vme_address_t *aspace, vme_cycle_t *cycle)
> +{
> +	unsigned int i, granularity = 0, ctl = 0;
> +	unsigned int vme_base_low, vme_base_high;
> +	unsigned int vme_bound_low, vme_bound_high;
> +	unsigned int pci_offset_low, pci_offset_high;
> +	unsigned long long vme_bound, pci_offset;
> +
> +
> +	i = image->number;
> +
> +	/* Read registers */
> +	ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITAT);
> +
> +	vme_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITSAU);
> +	vme_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITSAL);
> +	vme_bound_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITEAU);
> +	vme_bound_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITEAL);
> +	pci_offset_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITOFU);
> +	pci_offset_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +		TSI148_LCSR_OFFSET_ITOFL);
> +
> +	/* Convert 64-bit variables to 2x 32-bit variables */
> +	reg_join(vme_base_high, vme_base_low, vme_base);
> +	reg_join(vme_bound_high, vme_bound_low, &vme_bound);
> +	reg_join(pci_offset_high, pci_offset_low, &pci_offset);
> +
> +	*pci_base = (dma_addr_t)vme_base + pci_offset;
> +
> +	*enabled = 0;
> +	*aspace = 0;
> +	*cycle = 0;
> +
> +	if (ctl & TSI148_LCSR_ITAT_EN)
> +		*enabled = 1;
> +
> +	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A16) {
> +		granularity = 0x10;
> +		*aspace |= VME_A16;
> +	}
> +	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A24) {
> +		granularity = 0x1000;
> +		*aspace |= VME_A24;
> +	}
> +	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A32) {
> +		granularity = 0x10000;
> +		*aspace |= VME_A32;
> +	}
> +	if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A64) {
> +		granularity = 0x10000;
> +		*aspace |= VME_A64;
> +	}
> +
> +	/* Need granularity before we set the size */
> +	*size = (unsigned long long)((vme_bound - *vme_base) + granularity);
> +
> +
> +	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_160)
> +		*cycle |= VME_2eSST160;
> +	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_267)
> +		*cycle |= VME_2eSST267;
> +	if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_320)
> +		*cycle |= VME_2eSST320;
> +
> +	if (ctl & TSI148_LCSR_ITAT_BLT)
> +		*cycle |= VME_BLT;
> +	if (ctl & TSI148_LCSR_ITAT_MBLT)
> +		*cycle |= VME_MBLT;
> +	if (ctl & TSI148_LCSR_ITAT_2eVME)
> +		*cycle |= VME_2eVME;
> +	if (ctl & TSI148_LCSR_ITAT_2eSST)
> +		*cycle |= VME_2eSST;
> +	if (ctl & TSI148_LCSR_ITAT_2eSSTB)
> +		*cycle |= VME_2eSSTB;
> +
> +	if (ctl & TSI148_LCSR_ITAT_SUPR)
> +		*cycle |= VME_SUPER;
> +	if (ctl & TSI148_LCSR_ITAT_NPRIV)
> +		*cycle |= VME_USER;
> +	if (ctl & TSI148_LCSR_ITAT_PGM)
> +		*cycle |= VME_PROG;
> +	if (ctl & TSI148_LCSR_ITAT_DATA)
> +		*cycle |= VME_DATA;
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate and map PCI Resource
> + */
> +static int tsi148_alloc_resource(struct vme_master_resource *image,
> +	unsigned long long size)
> +{
> +	unsigned long long existing_size;
> +	int retval = 0;
> +	struct pci_dev *pdev;
> +
> +	/* Find pci_dev container of dev */
> +        if (tsi148_bridge->parent == NULL) {
> +                printk("Dev entry NULL\n");
> +                return -EINVAL;
> +        }
> +        pdev = container_of(tsi148_bridge->parent, struct pci_dev, dev);
> +
> +	existing_size = (unsigned long long)(image->pci_resource.end -
> +		image->pci_resource.start);
> +
> +	/* If the existing size is OK, return */
> +	if (existing_size == (size - 1))
> +		return 0;
> +
> +	if (existing_size != 0) {
> +		iounmap(image->kern_base);
> +		image->kern_base = NULL;
> +		if (image->pci_resource.name != NULL)
> +			kfree(image->pci_resource.name);
> +		release_resource(&(image->pci_resource));
> +		memset(&(image->pci_resource), 0, sizeof(struct resource));
> +	}
> +
> +	if (image->pci_resource.name == NULL) {
> +		image->pci_resource.name = kmalloc(VMENAMSIZ+3, GFP_KERNEL);
> +		if (image->pci_resource.name == NULL) {
> +			printk(KERN_ERR "Unable to allocate memory for resource"
> +				" name\n");
> +			retval = -ENOMEM;
> +			goto err_name;
> +		}
> +	}
> +
> +	sprintf((char *)image->pci_resource.name, "%s.%d", tsi148_bridge->name,
> +		image->number);
> +
> +	image->pci_resource.start = 0;
> +	image->pci_resource.end = (unsigned long)size;
> +	image->pci_resource.flags = IORESOURCE_MEM;
> +
> +	retval = pci_bus_alloc_resource(pdev->bus,
> +		&(image->pci_resource), size, size, PCIBIOS_MIN_MEM,
> +		0, NULL, NULL);
> +	if (retval) {
> +		printk(KERN_ERR "Failed to allocate mem resource for "
> +			"window %d size 0x%lx start 0x%lx\n",
> +			image->number, (unsigned long)size,
> +			(unsigned long)image->pci_resource.start);
> +		goto err_resource;
> +	}
> +
> +	image->kern_base = ioremap_nocache(
> +		image->pci_resource.start, size);
> +	if (image->kern_base == NULL) {
> +		printk(KERN_ERR "Failed to remap resource\n");
> +		retval = -ENOMEM;
> +		goto err_remap;
> +	}
> +
> +	return 0;
> +
> +	iounmap(image->kern_base);
> +	image->kern_base = NULL;
> +err_remap:
> +	release_resource(&(image->pci_resource));
> +err_resource:
> +	kfree(image->pci_resource.name);
> +	memset(&(image->pci_resource), 0, sizeof(struct resource));
> +err_name:
> +	return retval;
> +}
> +
> +/*
> + * Free and unmap PCI Resource
> + */
> +static void tsi148_free_resource(struct vme_master_resource *image)
> +{
> +	iounmap(image->kern_base);
> +	image->kern_base = NULL;
> +	release_resource(&(image->pci_resource));
> +	kfree(image->pci_resource.name);
> +	memset(&(image->pci_resource), 0, sizeof(struct resource));
> +}
> +
> +/*
> + * Set the attributes of an outbound window.
> + */
> +int tsi148_master_set( struct vme_master_resource *image, int enabled,
> +	unsigned long long vme_base, unsigned long long size,
> +	vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	int retval = 0;
> +	unsigned int i;
> +	unsigned int temp_ctl = 0;
> +	unsigned int pci_base_low, pci_base_high;
> +	unsigned int pci_bound_low, pci_bound_high;
> +	unsigned int vme_offset_low, vme_offset_high;
> +	unsigned long long pci_bound, vme_offset, pci_base;
> +
> +	/* Verify input data */
> +	if (vme_base & 0xFFFF) {
> +		printk("Invalid VME Window alignment\n");
> +		retval = -EINVAL;
> +		goto err_window;
> +	}
> +	if (size < 0x10000) {
> +		printk("Invalid VME Window size\n");
> +		retval = -EINVAL;
> +		goto err_window;
> +	}
> +
> +	spin_lock(&(image->lock));
> +
> +	/* Let's allocate the resource here rather than further up the stack as
> +	 * it avoids pushing loads of bus dependant stuff up the stack
> +	 */
> +	retval = tsi148_alloc_resource(image, size);
> +	if (retval) {
> +		spin_unlock(&(image->lock));
> +		printk(KERN_ERR "Unable to allocate memory for resource "
> +			"name\n");
> +		retval = -ENOMEM;
> +		goto err_res;
> +	}
> +
> +	pci_base = (unsigned long long)image->pci_resource.start;
> +
> +
> +	/*
> +	 * Bound address is a valid address for the window, adjust
> +	 * according to window granularity.
> +	 */
> +	pci_bound = pci_base + (size - 0x10000);
> +	vme_offset = vme_base - pci_base;
> +
> +	/* Convert 64-bit variables to 2x 32-bit variables */
> +	reg_split(pci_base, &pci_base_high, &pci_base_low);
> +	reg_split(pci_bound, &pci_bound_high, &pci_bound_low);
> +	reg_split(vme_offset, &vme_offset_high, &vme_offset_low);
> +
> +	if (pci_base_low & 0xFFFF) {
> +		spin_unlock(&(image->lock));
> +		printk("Invalid PCI base alignment\n");
> +		retval = -EINVAL;
> +		goto err_gran;
> +	}
> +	if (pci_bound_low & 0xFFFF) {
> +		spin_unlock(&(image->lock));
> +		printk("Invalid PCI bound alignment\n");
> +		retval = -EINVAL;
> +		goto err_gran;
> +	}
> +	if (vme_offset_low & 0xFFFF) {
> +		spin_unlock(&(image->lock));
> +		printk("Invalid VME Offset alignment\n");
> +		retval = -EINVAL;
> +		goto err_gran;
> +	}
> +
> +	i = image->number;
> +
> +	/* Disable while we are mucking around */
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTAT);
> +	temp_ctl &= ~TSI148_LCSR_OTAT_EN;
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTAT);
> +
> +/* XXX Prefetch stuff currently unsupported */
> +#if 0
> +	if (vmeOut->prefetchEnable) {
> +		temp_ctl |= 0x40000;
> +		for (x = 0; x < 4; x++) {
> +			if ((2 << x) >= vmeOut->prefetchSize)
> +				break;
> +		}
> +		if (x == 4)
> +			x = 3;
> +		temp_ctl |= (x << 16);
> +	}
> +#endif
> +
> +	/* Setup 2eSST speeds */
> +	temp_ctl &= ~TSI148_LCSR_OTAT_2eSSTM_M;
> +	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
> +	case VME_2eSST160:
> +		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_160;
> +		break;
> +	case VME_2eSST267:
> +		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_267;
> +		break;
> +	case VME_2eSST320:
> +		temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_320;
> +		break;
> +	}
> +
> +	/* Setup cycle types */
> +	if (cycle & VME_BLT) {
> +		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
> +		temp_ctl |= TSI148_LCSR_OTAT_TM_BLT;
> +	}
> +	if (cycle & VME_MBLT) {
> +		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
> +		temp_ctl |= TSI148_LCSR_OTAT_TM_MBLT;
> +	}
> +	if (cycle & VME_2eVME) {
> +		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
> +		temp_ctl |= TSI148_LCSR_OTAT_TM_2eVME;
> +	}
> +	if (cycle & VME_2eSST) {
> +		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
> +		temp_ctl |= TSI148_LCSR_OTAT_TM_2eSST;
> +	}
> +	if (cycle & VME_2eSSTB) {
> +		printk("Currently not setting Broadcast Select Registers\n");
> +		temp_ctl &= ~TSI148_LCSR_OTAT_TM_M;
> +		temp_ctl |= TSI148_LCSR_OTAT_TM_2eSSTB;
> +	}
> +
> +	/* Setup data width */
> +	temp_ctl &= ~TSI148_LCSR_OTAT_DBW_M;
> +	switch (dwidth) {
> +	case VME_D16:
> +		temp_ctl |= TSI148_LCSR_OTAT_DBW_16;
> +		break;
> +	case VME_D32:
> +		temp_ctl |= TSI148_LCSR_OTAT_DBW_32;
> +		break;
> +	default:
> +		spin_unlock(&(image->lock));
> +		printk("Invalid data width\n");
> +		retval = -EINVAL;
> +		goto err_dwidth;
> +	}
> +
> +	/* Setup address space */
> +	temp_ctl &= ~TSI148_LCSR_OTAT_AMODE_M;
> +	switch (aspace) {
> +	case VME_A16:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A16;
> +		break;
> +	case VME_A24:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A24;
> +		break;
> +	case VME_A32:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A32;
> +		break;
> +	case VME_A64:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_A64;
> +		break;
> +	case VME_CRCSR:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_CRCSR;
> +		break;
> +	case VME_USER1:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER1;
> +		break;
> +	case VME_USER2:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER2;
> +		break;
> +	case VME_USER3:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER3;
> +		break;
> +	case VME_USER4:
> +		temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER4;
> +		break;
> +	default:
> +		spin_unlock(&(image->lock));
> +		printk("Invalid address space\n");
> +		retval = -EINVAL;
> +		goto err_aspace;
> +		break;
> +	}
> +
> +	temp_ctl &= ~(3<<4);
> +	if (cycle & VME_SUPER)
> +		temp_ctl |= TSI148_LCSR_OTAT_SUP;
> +	if (cycle & VME_PROG)
> +		temp_ctl |= TSI148_LCSR_OTAT_PGM;
> +
> +	/* Setup mapping */
> +	iowrite32be(pci_base_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAU);
> +	iowrite32be(pci_base_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAL);
> +	iowrite32be(pci_bound_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTEAU);
> +	iowrite32be(pci_bound_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTEAL);
> +	iowrite32be(vme_offset_high, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTOFU);
> +	iowrite32be(vme_offset_low, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTOFL);
> +
> +/* XXX We need to deal with OTBS */
> +#if 0
> +	iowrite32be(vmeOut->bcastSelect2esst, tsi148_bridge->base +
> +		TSI148_LCSR_OT[i] + TSI148_LCSR_OFFSET_OTBS);
> +#endif
> +
> +	/* Write ctl reg without enable */
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTAT);
> +
> +	if (enabled)
> +		temp_ctl |= TSI148_LCSR_OTAT_EN;
> +
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTAT);
> +
> +	spin_unlock(&(image->lock));
> +	return 0;
> +
> +err_aspace:
> +err_dwidth:
> +err_gran:
> +	tsi148_free_resource(image);
> +err_res:
> +err_window:
> +	return retval;
> +
> +}
> +
> +/*
> + * Set the attributes of an outbound window.
> + *
> + * XXX Not parsing prefetch information.
> + */
> +int __tsi148_master_get( struct vme_master_resource *image, int *enabled,
> +	unsigned long long *vme_base, unsigned long long *size,
> +	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
> +{
> +	unsigned int i, ctl;
> +	unsigned int pci_base_low, pci_base_high;
> +	unsigned int pci_bound_low, pci_bound_high;
> +	unsigned int vme_offset_low, vme_offset_high;
> +
> +	unsigned long long pci_base, pci_bound, vme_offset;
> +
> +	i = image->number;
> +
> +	ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTAT);
> +
> +	pci_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAU);
> +	pci_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAL);
> +	pci_bound_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTEAU);
> +	pci_bound_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTEAL);
> +	vme_offset_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTOFU);
> +	vme_offset_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTOFL);
> +
> +	/* Convert 64-bit variables to 2x 32-bit variables */
> +	reg_join(pci_base_high, pci_base_low, &pci_base);
> +	reg_join(pci_bound_high, pci_bound_low, &pci_bound);
> +	reg_join(vme_offset_high, vme_offset_low, &vme_offset);
> +
> +	*vme_base = pci_base + vme_offset;
> +	*size = (unsigned long long)(pci_bound - pci_base) + 0x10000;
> +
> +	*enabled = 0;
> +	*aspace = 0;
> +	*cycle = 0;
> +	*dwidth = 0;
> +
> +	if (ctl & TSI148_LCSR_OTAT_EN)
> +		*enabled = 1;
> +
> +	/* Setup address space */
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A16)
> +		*aspace |= VME_A16;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A24)
> +		*aspace |= VME_A24;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A32)
> +		*aspace |= VME_A32;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A64)
> +		*aspace |= VME_A64;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_CRCSR)
> +		*aspace |= VME_CRCSR;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER1)
> +		*aspace |= VME_USER1;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER2)
> +		*aspace |= VME_USER2;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER3)
> +		*aspace |= VME_USER3;
> +	if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER4)
> +		*aspace |= VME_USER4;
> +
> +	/* Setup 2eSST speeds */
> +	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_160)
> +		*cycle |= VME_2eSST160;
> +	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_267)
> +		*cycle |= VME_2eSST267;
> +	if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_320)
> +		*cycle |= VME_2eSST320;
> +
> +	/* Setup cycle types */
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_SCT)
> +		*cycle |= VME_SCT;
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_BLT)
> +		*cycle |= VME_BLT;
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_MBLT)
> +		*cycle |= VME_MBLT;
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eVME)
> +		*cycle |= VME_2eVME;
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eSST)
> +		*cycle |= VME_2eSST;
> +	if ((ctl & TSI148_LCSR_OTAT_TM_M ) == TSI148_LCSR_OTAT_TM_2eSSTB)
> +		*cycle |= VME_2eSSTB;
> +
> +	if (ctl & TSI148_LCSR_OTAT_SUP)
> +		*cycle |= VME_SUPER;
> +	else
> +		*cycle |= VME_USER;
> +
> +	if (ctl & TSI148_LCSR_OTAT_PGM)
> +		*cycle |= VME_PROG;
> +	else
> +		*cycle |= VME_DATA;
> +
> +	/* Setup data width */
> +	if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_16)
> +		*dwidth = VME_D16;
> +	if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_32)
> +		*dwidth = VME_D32;
> +
> +	return 0;
> +}
> +
> +
> +int tsi148_master_get( struct vme_master_resource *image, int *enabled,
> +	unsigned long long *vme_base, unsigned long long *size,
> +	vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
> +{
> +	int retval;
> +
> +	spin_lock(&(image->lock));
> +
> +	retval = __tsi148_master_get(image, enabled, vme_base, size, aspace,
> +		cycle, dwidth);
> +
> +	spin_unlock(&(image->lock));
> +
> +	return retval;
> +}
> +
> +ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
> +	size_t count, loff_t offset)
> +{
> +	int retval, enabled;
> +	unsigned long long vme_base, size;
> +	vme_address_t aspace;
> +	vme_cycle_t cycle;
> +	vme_width_t dwidth;
> +	struct vme_bus_error *vme_err = NULL;
> +
> +	spin_lock(&(image->lock));
> +
> +	memcpy_fromio(buf, image->kern_base + offset, (unsigned int)count);
> +	retval = count;
> +
> +	if (!err_chk)
> +		goto skip_chk;
> +
> +	__tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle,
> +		&dwidth);
> +
> +	vme_err = tsi148_find_error(aspace, vme_base + offset, count);
> +	if(vme_err != NULL) {
> +		dev_err(image->parent->parent, "First VME read error detected "
> +			"an at address 0x%llx\n", vme_err->address);
> +		retval = vme_err->address - (vme_base + offset);
> +		/* Clear down save errors in this address range */
> +		tsi148_clear_errors(aspace, vme_base + offset, count);
> +	}
> +
> +skip_chk:
> +	spin_unlock(&(image->lock));
> +
> +	return retval;
> +}
> +
> +
> +/* XXX We need to change vme_master_resource->sem to a spinlock so that read
> + *     and write functions can be used in an interrupt context
> + */
> +ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
> +	size_t count, loff_t offset)
> +{
> +	int retval = 0, enabled;
> +	unsigned long long vme_base, size;
> +	vme_address_t aspace;
> +	vme_cycle_t cycle;
> +	vme_width_t dwidth;
> +
> +	struct vme_bus_error *vme_err = NULL;
> +
> +	spin_lock(&(image->lock));
> +
> +	memcpy_toio(image->kern_base + offset, buf, (unsigned int)count);
> +	retval = count;
> +
> +	/*
> +	 * Writes are posted. We need to do a read on the VME bus to flush out
> +	 * all of the writes before we check for errors. We can't guarentee
> +	 * that reading the data we have just written is safe. It is believed
> +	 * that there isn't any read, write re-ordering, so we can read any
> +	 * location in VME space, so lets read the Device ID from the tsi148's
> +	 * own registers as mapped into CR/CSR space.
> +	 *
> +	 * We check for saved errors in the written address range/space.
> +	 */
> +
> +	if (!err_chk)
> +		goto skip_chk;
> +
> +	/*
> +	 * Get window info first, to maximise the time that the buffers may
> +	 * fluch on their own
> +	 */
> +	__tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle,
> +		&dwidth);
> +
> +	ioread16(flush_image->kern_base + 0x7F000);
> +
> +	vme_err = tsi148_find_error(aspace, vme_base + offset, count);
> +	if(vme_err != NULL) {
> +		printk("First VME write error detected an at address 0x%llx\n",
> +			vme_err->address);
> +		retval = vme_err->address - (vme_base + offset);
> +		/* Clear down save errors in this address range */
> +		tsi148_clear_errors(aspace, vme_base + offset, count);
> +	}
> +
> +skip_chk:
> +	spin_unlock(&(image->lock));
> +
> +	return retval;
> +}
> +
> +/*
> + * Perform an RMW cycle on the VME bus.
> + *
> + * Requires a previously configured master window, returns final value.
> + */
> +unsigned int tsi148_master_rmw(struct vme_master_resource *image,
> +	unsigned int mask, unsigned int compare, unsigned int swap,
> +	loff_t offset)
> +{
> +	unsigned long long pci_addr;
> +	unsigned int pci_addr_high, pci_addr_low;
> +	u32 tmp, result;
> +	int i;
> +
> +
> +	/* Find the PCI address that maps to the desired VME address */
> +	i = image->number;
> +
> +	/* Locking as we can only do one of these at a time */
> +	down(&(vme_rmw));
> +
> +	/* Lock image */
> +	spin_lock(&(image->lock));
> +
> +	pci_addr_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAU);
> +	pci_addr_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +		TSI148_LCSR_OFFSET_OTSAL);
> +
> +	reg_join(pci_addr_high, pci_addr_low, &pci_addr);
> +	reg_split(pci_addr + offset, &pci_addr_high, &pci_addr_low);
> +
> +	/* Configure registers */
> +	iowrite32be(mask, tsi148_bridge->base + TSI148_LCSR_RMWEN);
> +	iowrite32be(compare, tsi148_bridge->base + TSI148_LCSR_RMWC);
> +	iowrite32be(swap, tsi148_bridge->base + TSI148_LCSR_RMWS);
> +	iowrite32be(pci_addr_high, tsi148_bridge->base + TSI148_LCSR_RMWAU);
> +	iowrite32be(pci_addr_low, tsi148_bridge->base + TSI148_LCSR_RMWAL);
> +
> +	/* Enable RMW */
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +	tmp |= TSI148_LCSR_VMCTRL_RMWEN;
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +
> +	/* Kick process off with a read to the required address. */
> +	result = ioread32be(image->kern_base + offset);
> +
> +	/* Disable RMW */
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +	tmp &= ~TSI148_LCSR_VMCTRL_RMWEN;
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +
> +	spin_unlock(&(image->lock));
> +
> +	up(&(vme_rmw));
> +
> +	return result;
> +}
> +
> +static int tsi148_dma_set_vme_src_attributes (u32 *attr, vme_address_t aspace,
> +	vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	/* Setup 2eSST speeds */
> +	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
> +	case VME_2eSST160:
> +		*attr |= TSI148_LCSR_DSAT_2eSSTM_160;
> +		break;
> +	case VME_2eSST267:
> +		*attr |= TSI148_LCSR_DSAT_2eSSTM_267;
> +		break;
> +	case VME_2eSST320:
> +		*attr |= TSI148_LCSR_DSAT_2eSSTM_320;
> +		break;
> +	}
> +
> +	/* Setup cycle types */
> +	if (cycle & VME_SCT) {
> +		*attr |= TSI148_LCSR_DSAT_TM_SCT;
> +	}
> +	if (cycle & VME_BLT) {
> +		*attr |= TSI148_LCSR_DSAT_TM_BLT;
> +	}
> +	if (cycle & VME_MBLT) {
> +		*attr |= TSI148_LCSR_DSAT_TM_MBLT;
> +	}
> +	if (cycle & VME_2eVME) {
> +		*attr |= TSI148_LCSR_DSAT_TM_2eVME;
> +	}
> +	if (cycle & VME_2eSST) {
> +		*attr |= TSI148_LCSR_DSAT_TM_2eSST;
> +	}
> +	if (cycle & VME_2eSSTB) {
> +		printk("Currently not setting Broadcast Select Registers\n");
> +		*attr |= TSI148_LCSR_DSAT_TM_2eSSTB;
> +	}
> +
> +	/* Setup data width */
> +	switch (dwidth) {
> +	case VME_D16:
> +		*attr |= TSI148_LCSR_DSAT_DBW_16;
> +		break;
> +	case VME_D32:
> +		*attr |= TSI148_LCSR_DSAT_DBW_32;
> +		break;
> +	default:
> +		printk("Invalid data width\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Setup address space */
> +	switch (aspace) {
> +	case VME_A16:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_A16;
> +		break;
> +	case VME_A24:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_A24;
> +		break;
> +	case VME_A32:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_A32;
> +		break;
> +	case VME_A64:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_A64;
> +		break;
> +	case VME_CRCSR:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_CRCSR;
> +		break;
> +	case VME_USER1:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_USER1;
> +		break;
> +	case VME_USER2:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_USER2;
> +		break;
> +	case VME_USER3:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_USER3;
> +		break;
> +	case VME_USER4:
> +		*attr |= TSI148_LCSR_DSAT_AMODE_USER4;
> +		break;
> +	default:
> +		printk("Invalid address space\n");
> +		return -EINVAL;
> +		break;
> +	}
> +
> +	if (cycle & VME_SUPER)
> +		*attr |= TSI148_LCSR_DSAT_SUP;
> +	if (cycle & VME_PROG)
> +		*attr |= TSI148_LCSR_DSAT_PGM;
> +
> +	return 0;
> +}
> +
> +static int tsi148_dma_set_vme_dest_attributes(u32 *attr, vme_address_t aspace,
> +	vme_cycle_t cycle, vme_width_t dwidth)
> +{
> +	/* Setup 2eSST speeds */
> +	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
> +	case VME_2eSST160:
> +		*attr |= TSI148_LCSR_DDAT_2eSSTM_160;
> +		break;
> +	case VME_2eSST267:
> +		*attr |= TSI148_LCSR_DDAT_2eSSTM_267;
> +		break;
> +	case VME_2eSST320:
> +		*attr |= TSI148_LCSR_DDAT_2eSSTM_320;
> +		break;
> +	}
> +
> +	/* Setup cycle types */
> +	if (cycle & VME_SCT) {
> +		*attr |= TSI148_LCSR_DDAT_TM_SCT;
> +	}
> +	if (cycle & VME_BLT) {
> +		*attr |= TSI148_LCSR_DDAT_TM_BLT;
> +	}
> +	if (cycle & VME_MBLT) {
> +		*attr |= TSI148_LCSR_DDAT_TM_MBLT;
> +	}
> +	if (cycle & VME_2eVME) {
> +		*attr |= TSI148_LCSR_DDAT_TM_2eVME;
> +	}
> +	if (cycle & VME_2eSST) {
> +		*attr |= TSI148_LCSR_DDAT_TM_2eSST;
> +	}
> +	if (cycle & VME_2eSSTB) {
> +		printk("Currently not setting Broadcast Select Registers\n");
> +		*attr |= TSI148_LCSR_DDAT_TM_2eSSTB;
> +	}
> +
> +	/* Setup data width */
> +	switch (dwidth) {
> +	case VME_D16:
> +		*attr |= TSI148_LCSR_DDAT_DBW_16;
> +		break;
> +	case VME_D32:
> +		*attr |= TSI148_LCSR_DDAT_DBW_32;
> +		break;
> +	default:
> +		printk("Invalid data width\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Setup address space */
> +	switch (aspace) {
> +	case VME_A16:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_A16;
> +		break;
> +	case VME_A24:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_A24;
> +		break;
> +	case VME_A32:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_A32;
> +		break;
> +	case VME_A64:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_A64;
> +		break;
> +	case VME_CRCSR:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_CRCSR;
> +		break;
> +	case VME_USER1:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_USER1;
> +		break;
> +	case VME_USER2:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_USER2;
> +		break;
> +	case VME_USER3:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_USER3;
> +		break;
> +	case VME_USER4:
> +		*attr |= TSI148_LCSR_DDAT_AMODE_USER4;
> +		break;
> +	default:
> +		printk("Invalid address space\n");
> +		return -EINVAL;
> +		break;
> +	}
> +
> +	if (cycle & VME_SUPER)
> +		*attr |= TSI148_LCSR_DDAT_SUP;
> +	if (cycle & VME_PROG)
> +		*attr |= TSI148_LCSR_DDAT_PGM;
> +
> +	return 0;
> +}
> +
> +/*
> + * Add a link list descriptor to the list
> + *
> + * XXX Need to handle 2eSST Broadcast select bits
> + */
> +int tsi148_dma_list_add (struct vme_dma_list *list, struct vme_dma_attr *src,
> +	struct vme_dma_attr *dest, size_t count)
> +{
> +	struct tsi148_dma_entry *entry, *prev;
> +	u32 address_high, address_low;
> +	struct vme_dma_pattern *pattern_attr;
> +	struct vme_dma_pci *pci_attr;
> +	struct vme_dma_vme *vme_attr;
> +	dma_addr_t desc_ptr;
> +	int retval = 0;
> +
> +	/* XXX descriptor must be aligned on 64-bit boundaries */
> +	entry = (struct tsi148_dma_entry *)kmalloc(
> +		sizeof(struct tsi148_dma_entry), GFP_KERNEL);
> +	if (entry == NULL) {
> +		printk("Failed to allocate memory for dma resource "
> +			"structure\n");
> +		retval = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	/* Test descriptor alignment */
> +	if ((unsigned long)&(entry->descriptor) & 0x7) {
> +		printk("Descriptor not aligned to 8 byte boundary as "
> +			"required: %p\n", &(entry->descriptor));
> +		retval = -EINVAL;
> +		goto err_align;
> +	}
> +
> +	/* Given we are going to fill out the structure, we probably don't
> +	 * need to zero it, but better safe than sorry for now.
> +	 */
> +	memset(&(entry->descriptor), 0, sizeof(struct tsi148_dma_descriptor));
> +
> +	/* Fill out source part */
> +	switch (src->type) {
> +	case VME_DMA_PATTERN:
> +		pattern_attr = (struct vme_dma_pattern *)src->private;
> +
> +		entry->descriptor.dsal = pattern_attr->pattern;
> +		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PAT;
> +		/* Default behaviour is 32 bit pattern */
> +		if (pattern_attr->type & VME_DMA_PATTERN_BYTE) {
> +			entry->descriptor.dsat |= TSI148_LCSR_DSAT_PSZ;
> +		}
> +		/* It seems that the default behaviour is to increment */
> +		if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) == 0) {
> +			entry->descriptor.dsat |= TSI148_LCSR_DSAT_NIN;
> +		}
> +		break;
> +	case VME_DMA_PCI:
> +		pci_attr = (struct vme_dma_pci *)src->private;
> +
> +		reg_split((unsigned long long)pci_attr->address, &address_high,
> +			&address_low);
> +		entry->descriptor.dsau = address_high;
> +		entry->descriptor.dsal = address_low;
> +		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PCI;
> +		break;
> +	case VME_DMA_VME:
> +		vme_attr = (struct vme_dma_vme *)src->private;
> +
> +		reg_split((unsigned long long)vme_attr->address, &address_high,
> +			&address_low);
> +		entry->descriptor.dsau = address_high;
> +		entry->descriptor.dsal = address_low;
> +		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_VME;
> +
> +		retval = tsi148_dma_set_vme_src_attributes(
> +			&(entry->descriptor.dsat), vme_attr->aspace,
> +			vme_attr->cycle, vme_attr->dwidth);
> +		if(retval < 0 )
> +			goto err_source;
> +		break;
> +	default:
> +		printk("Invalid source type\n");
> +		retval = -EINVAL;
> +		goto err_source;
> +		break;
> +	}
> +
> +	/* Assume last link - this will be over-written by adding another */
> +	entry->descriptor.dnlau = 0;
> +	entry->descriptor.dnlal = TSI148_LCSR_DNLAL_LLA;
> +
> +
> +	/* Fill out destination part */
> +	switch (dest->type) {
> +	case VME_DMA_PCI:
> +		pci_attr = (struct vme_dma_pci *)dest->private;
> +
> +		reg_split((unsigned long long)pci_attr->address, &address_high,
> +			&address_low);
> +		entry->descriptor.ddau = address_high;
> +		entry->descriptor.ddal = address_low;
> +		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_PCI;
> +		break;
> +	case VME_DMA_VME:
> +		vme_attr = (struct vme_dma_vme *)dest->private;
> +
> +		reg_split((unsigned long long)vme_attr->address, &address_high,
> +			&address_low);
> +		entry->descriptor.ddau = address_high;
> +		entry->descriptor.ddal = address_low;
> +		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_VME;
> +
> +		retval = tsi148_dma_set_vme_dest_attributes(
> +			&(entry->descriptor.ddat), vme_attr->aspace,
> +			vme_attr->cycle, vme_attr->dwidth);
> +		if(retval < 0 )
> +			goto err_dest;
> +		break;
> +	default:
> +		printk("Invalid destination type\n");
> +		retval = -EINVAL;
> +		goto err_dest;
> +		break;
> +	}
> +
> +	/* Fill out count */
> +	entry->descriptor.dcnt = (u32)count;
> +
> +	/* Add to list */
> +	list_add_tail(&(entry->list), &(list->entries));
> +
> +	/* Fill out previous descriptors "Next Address" */
> +	if(entry->list.prev != &(list->entries)){
> +		prev = list_entry(entry->list.prev, struct tsi148_dma_entry,
> +			list);
> +		/* We need the bus address for the pointer */
> +		desc_ptr = virt_to_bus(&(entry->descriptor));
> +		reg_split(desc_ptr, &(prev->descriptor.dnlau),
> +			&(prev->descriptor.dnlal));
> +	}
> +
> +	return 0;
> +
> +err_dest:
> +err_source:
> +err_align:
> +		kfree(entry);
> +err_mem:
> +	return retval;
> +}
> +
> +/*
> + * Check to see if the provided DMA channel is busy.
> + */
> +static int tsi148_dma_busy(int channel)
> +{
> +	u32 tmp;
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
> +		TSI148_LCSR_OFFSET_DSTA);
> +
> +	if (tmp & TSI148_LCSR_DSTA_BSY)
> +		return 0;
> +	else
> +		return 1;
> +
> +}
> +
> +/*
> + * Execute a previously generated link list
> + *
> + * XXX Need to provide control register configuration.
> + */
> +int tsi148_dma_list_exec(struct vme_dma_list *list)
> +{
> +	struct vme_dma_resource *ctrlr;
> +	int channel, retval = 0;
> +	struct tsi148_dma_entry *entry;
> +	dma_addr_t bus_addr;
> +	u32 bus_addr_high, bus_addr_low;
> +	u32 val, dctlreg = 0;
> +#if 0
> +	int x;
> +#endif
> +
> +	ctrlr = list->parent;
> +
> +	down(&(ctrlr->sem));
> +
> +	channel = ctrlr->number;
> +
> +	if (! list_empty(&(ctrlr->running))) {
> +		/*
> +		 * XXX We have an active DMA transfer and currently haven't
> +		 *     sorted out the mechanism for "pending" DMA transfers.
> +		 *     Return busy.
> +		 */
> +		/* Need to add to pending here */
> +		up(&(ctrlr->sem));
> +		return -EBUSY;
> +	} else {
> +		list_add(&(list->list), &(ctrlr->running));
> +	}
> +#if 0
> +	/* XXX Still todo */
> +	for (x = 0; x < 8; x++) {	/* vme block size */
> +		if ((32 << x) >= vmeDma->maxVmeBlockSize) {
> +			break;
> +		}
> +	}
> +	if (x == 8)
> +		x = 7;
> +	dctlreg |= (x << 12);
> +
> +	for (x = 0; x < 8; x++) {	/* pci block size */
> +		if ((32 << x) >= vmeDma->maxPciBlockSize) {
> +			break;
> +		}
> +	}
> +	if (x == 8)
> +		x = 7;
> +	dctlreg |= (x << 4);
> +
> +	if (vmeDma->vmeBackOffTimer) {
> +		for (x = 1; x < 8; x++) {	/* vme timer */
> +			if ((1 << (x - 1)) >= vmeDma->vmeBackOffTimer) {
> +				break;
> +			}
> +		}
> +		if (x == 8)
> +			x = 7;
> +		dctlreg |= (x << 8);
> +	}
> +
> +	if (vmeDma->pciBackOffTimer) {
> +		for (x = 1; x < 8; x++) {	/* pci timer */
> +			if ((1 << (x - 1)) >= vmeDma->pciBackOffTimer) {
> +				break;
> +			}
> +		}
> +		if (x == 8)
> +			x = 7;
> +		dctlreg |= (x << 0);
> +	}
> +#endif
> +
> +	/* Get first bus address and write into registers */
> +	entry = list_first_entry(&(list->entries), struct tsi148_dma_entry,
> +		list);
> +
> +	bus_addr = virt_to_bus(&(entry->descriptor));
> +
> +	up(&(ctrlr->sem));
> +
> +	reg_split(bus_addr, &bus_addr_high, &bus_addr_low);
> +
> +	iowrite32be(bus_addr_high, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU);
> +	iowrite32be(bus_addr_low, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL);
> +
> +	/* Start the operation */
> +	iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
> +
> +	wait_event_interruptible(dma_queue[channel], tsi148_dma_busy(channel));
> +	/*
> +	 * Read status register, this register is valid until we kick off a
> +	 * new transfer.
> +	 */
> +	val = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
> +		TSI148_LCSR_OFFSET_DSTA);
> +
> +	if (val & TSI148_LCSR_DSTA_VBE) {
> +		printk(KERN_ERR "tsi148: DMA Error. DSTA=%08X\n", val);
> +		retval = -EIO;
> +	}
> +
> +	/* Remove list from running list */
> +	down(&(ctrlr->sem));
> +	list_del(&(list->list));
> +	up(&(ctrlr->sem));
> +
> +	return retval;
> +}
> +
> +/*
> + * Clean up a previously generated link list
> + *
> + * We have a separate function, don't assume that the chain can't be reused.
> + */
> +int tsi148_dma_list_empty(struct vme_dma_list *list)
> +{
> +	struct list_head *pos, *temp;
> +        struct tsi148_dma_entry *entry;
> +
> +	/* detach and free each entry */
> +	list_for_each_safe(pos, temp, &(list->entries)) {
> +		list_del(pos);
> +		entry = list_entry(pos, struct tsi148_dma_entry, list);
> +		kfree(entry);
> +	}
> +
> +	return (0);
> +}
> +
> +/*
> + * All 4 location monitors reside at the same base - this is therefore a
> + * system wide configuration.
> + *
> + * This does not enable the LM monitor - that should be done when the first
> + * callback is attached and disabled when the last callback is removed.
> + */
> +int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace,
> +	vme_cycle_t cycle)
> +{
> +	u32 lm_base_high, lm_base_low, lm_ctl = 0;
> +	int i;
> +
> +	/* Get semaphore */
> +	down(&(vme_lm));
> +
> +	/* If we already have a callback attached, we can't move it! */
> +	for (i = 0; i < 4; i++) {
> +		if(lm_callback[i] != NULL) {
> +			up(&(vme_lm));
> +			printk("Location monitor callback attached, can't "
> +				"reset\n");
> +			return -EBUSY;
> +		}
> +	}
> +
> +	switch (aspace) {
> +	case VME_A16:
> +		lm_ctl |= TSI148_LCSR_LMAT_AS_A16;
> +		break;
> +	case VME_A24:
> +		lm_ctl |= TSI148_LCSR_LMAT_AS_A24;
> +		break;
> +	case VME_A32:
> +		lm_ctl |= TSI148_LCSR_LMAT_AS_A32;
> +		break;
> +	case VME_A64:
> +		lm_ctl |= TSI148_LCSR_LMAT_AS_A64;
> +		break;
> +	default:
> +		up(&(vme_lm));
> +		printk("Invalid address space\n");
> +		return -EINVAL;
> +		break;
> +	}
> +
> +	if (cycle & VME_SUPER)
> +		lm_ctl |= TSI148_LCSR_LMAT_SUPR ;
> +	if (cycle & VME_USER)
> +		lm_ctl |= TSI148_LCSR_LMAT_NPRIV;
> +	if (cycle & VME_PROG)
> +		lm_ctl |= TSI148_LCSR_LMAT_PGM;
> +	if (cycle & VME_DATA)
> +		lm_ctl |= TSI148_LCSR_LMAT_DATA;
> +
> +	reg_split(lm_base, &lm_base_high, &lm_base_low);
> +
> +	iowrite32be(lm_base_high, tsi148_bridge->base + TSI148_LCSR_LMBAU);
> +	iowrite32be(lm_base_low, tsi148_bridge->base + TSI148_LCSR_LMBAL);
> +	iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
> +
> +	up(&(vme_lm));
> +
> +	return 0;
> +}
> +
> +/* Get configuration of the callback monitor and return whether it is enabled
> + * or disabled.
> + */
> +int tsi148_lm_get(unsigned long long *lm_base, vme_address_t *aspace,
> +	vme_cycle_t *cycle)
> +{
> +	u32 lm_base_high, lm_base_low, lm_ctl, enabled = 0;
> +
> +	/* Get semaphore */
> +	down(&(vme_lm));
> +
> +	lm_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAU);
> +	lm_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAL);
> +	lm_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
> +
> +	reg_join(lm_base_high, lm_base_low, lm_base);
> +
> +	if (lm_ctl & TSI148_LCSR_LMAT_EN)
> +		enabled = 1;
> +
> +	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A16) {
> +		*aspace |= VME_A16;
> +	}
> +	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A24) {
> +		*aspace |= VME_A24;
> +	}
> +	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A32) {
> +		*aspace |= VME_A32;
> +	}
> +	if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A64) {
> +		*aspace |= VME_A64;
> +	}
> +
> +	if (lm_ctl & TSI148_LCSR_LMAT_SUPR)
> +		*cycle |= VME_SUPER;
> +	if (lm_ctl & TSI148_LCSR_LMAT_NPRIV)
> +		*cycle |= VME_USER;
> +	if (lm_ctl & TSI148_LCSR_LMAT_PGM)
> +		*cycle |= VME_PROG;
> +	if (lm_ctl & TSI148_LCSR_LMAT_DATA)
> +		*cycle |= VME_DATA;
> +
> +	up(&(vme_lm));
> +
> +	return enabled;
> +}
> +
> +/*
> + * Attach a callback to a specific location monitor.
> + *
> + * Callback will be passed the monitor triggered.
> + */
> +int tsi148_lm_attach(int monitor, void (*callback)(int))
> +{
> +	u32 lm_ctl, tmp;
> +
> +	/* Get semaphore */
> +	down(&(vme_lm));
> +
> +	/* Ensure that the location monitor is configured - need PGM or DATA */
> +	lm_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
> +	if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) == 0) {
> +		up(&(vme_lm));
> +		printk("Location monitor not properly configured\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check that a callback isn't already attached */
> +	if (lm_callback[monitor] != NULL) {
> +		up(&(vme_lm));
> +		printk("Existing callback attached\n");
> +		return -EBUSY;
> +	}
> +
> +	/* Attach callback */
> +	lm_callback[monitor] = callback;
> +
> +	/* Enable Location Monitor interrupt */
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
> +	tmp |= TSI148_LCSR_INTEN_LMEN[monitor];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	tmp |= TSI148_LCSR_INTEO_LMEO[monitor];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +	/* Ensure that global Location Monitor Enable set */
> +	if ((lm_ctl & TSI148_LCSR_LMAT_EN) == 0) {
> +		lm_ctl |= TSI148_LCSR_LMAT_EN;
> +		iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
> +	}
> +
> +	up(&(vme_lm));
> +
> +	return 0;
> +}
> +
> +/*
> + * Detach a callback function forn a specific location monitor.
> + */
> +int tsi148_lm_detach(int monitor)
> +{
> +	u32 lm_en, tmp;
> +
> +	/* Get semaphore */
> +	down(&(vme_lm));
> +
> +	/* Disable Location Monitor and ensure previous interrupts are clear */
> +	lm_en = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
> +	lm_en &= ~TSI148_LCSR_INTEN_LMEN[monitor];
> +	iowrite32be(lm_en, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	tmp &= ~TSI148_LCSR_INTEO_LMEO[monitor];
> +	iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +	iowrite32be(TSI148_LCSR_INTC_LMC[monitor],
> +		 tsi148_bridge->base + TSI148_LCSR_INTEO);
> +
> +	/* Detach callback */
> +	lm_callback[monitor] = NULL;
> +
> +	/* If all location monitors disabled, disable global Location Monitor */
> +	if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S |
> +			TSI148_LCSR_INTS_LM2S | TSI148_LCSR_INTS_LM3S)) == 0) {
> +		tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
> +		tmp &= ~TSI148_LCSR_LMAT_EN;
> +		iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_LMAT);
> +	}
> +
> +	up(&(vme_lm));
> +
> +	return 0;
> +}
> +
> +/*
> + * Determine Geographical Addressing
> + */
> +int tsi148_slot_get(void)
> +{
> +        u32 slot = 0;
> +
> +	slot = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
> +	slot = slot & TSI148_LCSR_VSTAT_GA_M;
> +	return (int)slot;
> +}
> +
> +static int __init tsi148_init(void)
> +{
> +	return pci_register_driver(&tsi148_driver);
> +}
> +
> +/*
> + * Configure CR/CSR space
> + *
> + * Access to the CR/CSR can be configured at power-up. The location of the
> + * CR/CSR registers in the CR/CSR address space is determined by the boards
> + * Auto-ID or Geographic address. This function ensures that the window is
> + * enabled at an offset consistent with the boards geopgraphic address.
> + *
> + * Each board has a 512kB window, with the highest 4kB being used for the
> + * boards registers, this means there is a fix length 508kB window which must
> + * be mapped onto PCI memory.
> + */
> +static int tsi148_crcsr_init(struct pci_dev *pdev)
> +{
> +	u32 cbar, crat, vstat;
> +	u32 crcsr_bus_high, crcsr_bus_low;
> +	int retval;
> +
> +	/* Allocate mem for CR/CSR image */
> +	crcsr_kernel = pci_alloc_consistent(pdev, VME_CRCSR_BUF_SIZE,
> +		&crcsr_bus);
> +	if (crcsr_kernel == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory for CR/CSR "
> +			"image\n");
> +		return -ENOMEM;
> +	}
> +
> +	memset(crcsr_kernel, 0, VME_CRCSR_BUF_SIZE);
> +
> +	reg_split(crcsr_bus, &crcsr_bus_high, &crcsr_bus_low);
> +
> +	iowrite32be(crcsr_bus_high, tsi148_bridge->base + TSI148_LCSR_CROU);
> +	iowrite32be(crcsr_bus_low, tsi148_bridge->base + TSI148_LCSR_CROL);
> +
> +	/* Ensure that the CR/CSR is configured at the correct offset */
> +	cbar = ioread32be(tsi148_bridge->base + TSI148_CBAR);
> +	cbar = (cbar & TSI148_CRCSR_CBAR_M)>>3;
> +
> +	vstat = tsi148_slot_get();
> +
> +	if (cbar != vstat) {
> +		dev_info(&pdev->dev, "Setting CR/CSR offset\n");
> +		iowrite32be(cbar<<3, tsi148_bridge->base + TSI148_CBAR);
> +	}
> +	dev_info(&pdev->dev, "CR/CSR Offset: %d\n", cbar);
> +
> +	crat = ioread32be(tsi148_bridge->base + TSI148_LCSR_CRAT);
> +	if (crat & TSI148_LCSR_CRAT_EN) {
> +		dev_info(&pdev->dev, "Enabling CR/CSR space\n");
> +		iowrite32be(crat | TSI148_LCSR_CRAT_EN,
> +			tsi148_bridge->base + TSI148_LCSR_CRAT);
> +	} else
> +		dev_info(&pdev->dev, "CR/CSR already enabled\n");
> +
> +	/* If we want flushed, error-checked writes, set up a window
> +	 * over the CR/CSR registers. We read from here to safely flush
> +	 * through VME writes.
> +	 */
> +	if(err_chk) {
> +		retval = tsi148_master_set(flush_image, 1, (vstat * 0x80000),
> +			0x80000, VME_CRCSR, VME_SCT, VME_D16);
> +		if (retval)
> +			dev_err(&pdev->dev, "Configuring flush image failed\n");
> +	}
> +
> +	return 0;
> +
> +}
> +
> +static void tsi148_crcsr_exit(struct pci_dev *pdev)
> +{
> +	u32 crat;
> +
> +	/* Turn off CR/CSR space */
> +	crat = ioread32be(tsi148_bridge->base + TSI148_LCSR_CRAT);
> +	iowrite32be(crat & ~TSI148_LCSR_CRAT_EN,
> +		tsi148_bridge->base + TSI148_LCSR_CRAT);
> +
> +	/* Free image */
> +	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CROU);
> +	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CROL);
> +
> +	pci_free_consistent(pdev, VME_CRCSR_BUF_SIZE, crcsr_kernel, crcsr_bus);
> +}
> +
> +static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> +	int retval, i, master_num;
> +	u32 data;
> +	struct list_head *pos = NULL;
> +	struct vme_master_resource *master_image;
> +	struct vme_slave_resource *slave_image;
> +	struct vme_dma_resource *dma_ctrlr;
> +
> +	/* If we want to support more than one of each bridge, we need to
> +	 * dynamically generate this so we get one per device
> +	 */
> +	tsi148_bridge = (struct vme_bridge *)kmalloc(sizeof(struct vme_bridge),
> +		GFP_KERNEL);
> +	if (tsi148_bridge == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory for device "
> +			"structure\n");
> +		retval = -ENOMEM;
> +		goto err_struct;
> +	}
> +
> +	memset(tsi148_bridge, 0, sizeof(struct vme_bridge));
> +
> +	/* Enable the device */
> +	retval = pci_enable_device(pdev);
> +	if (retval) {
> +		dev_err(&pdev->dev, "Unable to enable device\n");
> +		goto err_enable;
> +	}
> +
> +	/* Map Registers */
> +	retval = pci_request_regions(pdev, driver_name);
> +	if (retval) {
> +		dev_err(&pdev->dev, "Unable to reserve resources\n");
> +		goto err_resource;
> +	}
> +
> +	/* map registers in BAR 0 */
> +	tsi148_bridge->base = ioremap_nocache(pci_resource_start(pdev, 0), 4096);
> +	if (!tsi148_bridge->base) {
> +		dev_err(&pdev->dev, "Unable to remap CRG region\n");
> +		retval = -EIO;
> +		goto err_remap;
> +	}
> +
> +	/* Check to see if the mapping worked out */
> +	data = ioread32(tsi148_bridge->base + TSI148_PCFS_ID) & 0x0000FFFF;
> +	if (data != PCI_VENDOR_ID_TUNDRA) {
> +		dev_err(&pdev->dev, "CRG region check failed\n");
> +		retval = -EIO;
> +		goto err_test;
> +	}
> +
> +	/* Initialize wait queues & mutual exclusion flags */
> +	/* XXX These need to be moved to the vme_bridge structure */
> +	init_waitqueue_head(&dma_queue[0]);
> +	init_waitqueue_head(&dma_queue[1]);
> +	init_waitqueue_head(&iack_queue);
> +	init_MUTEX(&(vme_int));
> +	init_MUTEX(&(vme_irq));
> +	init_MUTEX(&(vme_rmw));
> +	init_MUTEX(&(vme_lm));
> +
> +	tsi148_bridge->parent = &(pdev->dev);
> +	strcpy(tsi148_bridge->name, driver_name);
> +
> +	/* Setup IRQ */
> +	retval = tsi148_irq_init(tsi148_bridge);
> +	if (retval != 0) {
> +		dev_err(&pdev->dev, "Chip Initialization failed.\n");
> +		goto err_irq;
> +	}
> +
> +	/* If we are going to flush writes, we need to read from the VME bus.
> +	 * We need to do this safely, thus we read the devices own CR/CSR
> +	 * register. To do this we must set up a window in CR/CSR space and
> +	 * hence have one less master window resource available.
> +	 */
> +	master_num = TSI148_MAX_MASTER;
> +	if(err_chk){
> +		master_num--;
> +		/* XXX */
> +		flush_image = (struct vme_master_resource *)kmalloc(
> +			sizeof(struct vme_master_resource), GFP_KERNEL);
> +		if (flush_image == NULL) {
> +			dev_err(&pdev->dev, "Failed to allocate memory for "
> +			"flush resource structure\n");
> +			retval = -ENOMEM;
> +			goto err_master;
> +		}
> +		flush_image->parent = tsi148_bridge;
> +		spin_lock_init(&(flush_image->lock));
> +		flush_image->locked = 1;
> +		flush_image->number = master_num;
> +		flush_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
> +			VME_A64;
> +		flush_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
> +			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
> +			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
> +			VME_PROG | VME_DATA;
> +		flush_image->width_attr = VME_D16 | VME_D32;
> +		memset(&(flush_image->pci_resource), 0,
> +			sizeof(struct resource));
> +		flush_image->kern_base  = NULL;
> +	}
> +
> +	/* Add master windows to list */
> +	INIT_LIST_HEAD(&(tsi148_bridge->master_resources));
> +	for (i = 0; i < master_num; i++) {
> +		master_image = (struct vme_master_resource *)kmalloc(
> +			sizeof(struct vme_master_resource), GFP_KERNEL);
> +		if (master_image == NULL) {
> +			dev_err(&pdev->dev, "Failed to allocate memory for "
> +			"master resource structure\n");
> +			retval = -ENOMEM;
> +			goto err_master;
> +		}
> +		master_image->parent = tsi148_bridge;
> +		spin_lock_init(&(master_image->lock));
> +		master_image->locked = 0;
> +		master_image->number = i;
> +		master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
> +			VME_A64;
> +		master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
> +			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
> +			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
> +			VME_PROG | VME_DATA;
> +		master_image->width_attr = VME_D16 | VME_D32;
> +		memset(&(master_image->pci_resource), 0,
> +			sizeof(struct resource));
> +		master_image->kern_base  = NULL;
> +		list_add_tail(&(master_image->list),
> +			&(tsi148_bridge->master_resources));
> +	}
> +
> +	/* Add slave windows to list */
> +	INIT_LIST_HEAD(&(tsi148_bridge->slave_resources));
> +	for (i = 0; i < TSI148_MAX_SLAVE; i++) {
> +		slave_image = (struct vme_slave_resource *)kmalloc(
> +			sizeof(struct vme_slave_resource), GFP_KERNEL);
> +		if (slave_image == NULL) {
> +			dev_err(&pdev->dev, "Failed to allocate memory for "
> +			"slave resource structure\n");
> +			retval = -ENOMEM;
> +			goto err_slave;
> +		}
> +		slave_image->parent = tsi148_bridge;
> +		init_MUTEX(&(slave_image->sem));
> +		slave_image->locked = 0;
> +		slave_image->number = i;
> +		slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
> +			VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
> +			VME_USER3 | VME_USER4;
> +		slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
> +			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
> +			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
> +			VME_PROG | VME_DATA;
> +		list_add_tail(&(slave_image->list),
> +			&(tsi148_bridge->slave_resources));
> +	}
> +
> +	/* Add dma engines to list */
> +	INIT_LIST_HEAD(&(tsi148_bridge->dma_resources));
> +	for (i = 0; i < TSI148_MAX_DMA; i++) {
> +		dma_ctrlr = (struct vme_dma_resource *)kmalloc(
> +			sizeof(struct vme_dma_resource), GFP_KERNEL);
> +		if (dma_ctrlr == NULL) {
> +			dev_err(&pdev->dev, "Failed to allocate memory for "
> +			"dma resource structure\n");
> +			retval = -ENOMEM;
> +			goto err_dma;
> +		}
> +		dma_ctrlr->parent = tsi148_bridge;
> +		init_MUTEX(&(dma_ctrlr->sem));
> +		dma_ctrlr->locked = 0;
> +		dma_ctrlr->number = i;
> +		INIT_LIST_HEAD(&(dma_ctrlr->pending));
> +		INIT_LIST_HEAD(&(dma_ctrlr->running));
> +		list_add_tail(&(dma_ctrlr->list),
> +			&(tsi148_bridge->dma_resources));
> +	}
> +
> +	tsi148_bridge->slave_get = tsi148_slave_get;
> +	tsi148_bridge->slave_set = tsi148_slave_set;
> +	tsi148_bridge->master_get = tsi148_master_get;
> +	tsi148_bridge->master_set = tsi148_master_set;
> +	tsi148_bridge->master_read = tsi148_master_read;
> +	tsi148_bridge->master_write = tsi148_master_write;
> +	tsi148_bridge->master_rmw = tsi148_master_rmw;
> +	tsi148_bridge->dma_list_add = tsi148_dma_list_add;
> +	tsi148_bridge->dma_list_exec = tsi148_dma_list_exec;
> +	tsi148_bridge->dma_list_empty = tsi148_dma_list_empty;
> +	tsi148_bridge->request_irq = tsi148_request_irq;
> +	tsi148_bridge->free_irq = tsi148_free_irq;
> +	tsi148_bridge->generate_irq = tsi148_generate_irq;
> +	tsi148_bridge->lm_set = tsi148_lm_set;
> +	tsi148_bridge->lm_get = tsi148_lm_get;
> +	tsi148_bridge->lm_attach = tsi148_lm_attach;
> +	tsi148_bridge->lm_detach = tsi148_lm_detach;
> +	tsi148_bridge->slot_get = tsi148_slot_get;
> +
> +	data = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
> +	dev_info(&pdev->dev, "Board is%s the VME system controller\n",
> +		(data & TSI148_LCSR_VSTAT_SCONS)? "" : " not");
> +	dev_info(&pdev->dev, "VME geographical address is %d\n",
> +		data & TSI148_LCSR_VSTAT_GA_M);
> +	dev_info(&pdev->dev, "VME Write and flush and error check is %s\n",
> +		err_chk ? "enabled" : "disabled");
> +
> +	if(tsi148_crcsr_init(pdev)) {
> +		dev_err(&pdev->dev, "CR/CSR configuration failed.\n");
> +		goto err_crcsr;
> +
> +	}
> +
> +	/* Need to save tsi148_bridge pointer locally in link list for use in
> +	 * tsi148_remove()
> +	 */
> +	retval = vme_register_bridge(tsi148_bridge);
> +	if (retval != 0) {
> +		dev_err(&pdev->dev, "Chip Registration failed.\n");
> +		goto err_reg;
> +	}
> +
> +	/* Clear VME bus "board fail", and "power-up reset" lines */
> +	data = ioread32be(tsi148_bridge->base + TSI148_LCSR_VSTAT);
> +	data &= ~TSI148_LCSR_VSTAT_BRDFL;
> +	data |= TSI148_LCSR_VSTAT_CPURST;
> +	iowrite32be(data, tsi148_bridge->base + TSI148_LCSR_VSTAT);
> +
> +	return 0;
> +
> +	vme_unregister_bridge(tsi148_bridge);
> +err_reg:
> +	tsi148_crcsr_exit(pdev);
> +err_crcsr:
> +err_dma:
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->dma_resources)) {
> +		dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
> +		list_del(pos);
> +		kfree(dma_ctrlr);
> +	}
> +err_slave:
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->slave_resources)) {
> +		slave_image = list_entry(pos, struct vme_slave_resource, list);
> +		list_del(pos);
> +		kfree(slave_image);
> +	}
> +err_master:
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->master_resources)) {
> +		master_image = list_entry(pos, struct vme_master_resource,				list);
> +		list_del(pos);
> +		kfree(master_image);
> +	}
> +
> +	tsi148_irq_exit(pdev);
> +err_irq:
> +err_test:
> +	iounmap(tsi148_bridge->base);
> +err_remap:
> +	pci_release_regions(pdev);
> +err_resource:
> +	pci_disable_device(pdev);
> +err_enable:
> +	kfree(tsi148_bridge);
> +err_struct:
> +	return retval;
> +
> +}
> +
> +static void tsi148_remove(struct pci_dev *pdev)
> +{
> +	struct list_head *pos = NULL;
> +	struct vme_master_resource *master_image;
> +	struct vme_slave_resource *slave_image;
> +	struct vme_dma_resource *dma_ctrlr;
> +	int i;
> +
> +	dev_dbg(&pdev->dev, "Driver is being unloaded.\n");
> +
> +	/* XXX We need to find the pdev->dev in the list of vme_bridge->dev's */
> +
> +	/*
> +	 *  Shutdown all inbound and outbound windows.
> +	 */
> +	for (i = 0; i < 8; i++) {
> +		iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_IT[i] +
> +			TSI148_LCSR_OFFSET_ITAT);
> +		iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_OT[i] +
> +			TSI148_LCSR_OFFSET_OTAT);
> +	}
> +
> +	/*
> +	 *  Shutdown Location monitor.
> +	 */
> +	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_LMAT);
> +
> +	/*
> +	 *  Shutdown CRG map.
> +	 */
> +	iowrite32be(0, tsi148_bridge->base + TSI148_LCSR_CSRAT);
> +
> +	/*
> +	 *  Clear error status.
> +	 */
> +	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_EDPAT);
> +	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_VEAT);
> +	iowrite32be(0x07000700, tsi148_bridge->base + TSI148_LCSR_PSTAT);
> +
> +	/*
> +	 *  Remove VIRQ interrupt (if any)
> +	 */
> +	if (ioread32be(tsi148_bridge->base + TSI148_LCSR_VICR) & 0x800) {
> +		iowrite32be(0x8000, tsi148_bridge->base + TSI148_LCSR_VICR);
> +	}
> +
> +	/*
> +	 *  Disable and clear all interrupts.
> +	 */
> +	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTEO);
> +	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTC);
> +	iowrite32be(0xFFFFFFFF, tsi148_bridge->base + TSI148_LCSR_INTEN);
> +
> +	/*
> +	 *  Map all Interrupts to PCI INTA
> +	 */
> +	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTM1);
> +	iowrite32be(0x0, tsi148_bridge->base + TSI148_LCSR_INTM2);
> +
> +	tsi148_irq_exit(pdev);
> +
> +	vme_unregister_bridge(tsi148_bridge);
> +
> +	tsi148_crcsr_exit(pdev);
> +
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->dma_resources)) {
> +		dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
> +		list_del(pos);
> +		kfree(dma_ctrlr);
> +	}
> +
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->slave_resources)) {
> +		slave_image = list_entry(pos, struct vme_slave_resource, list);
> +		list_del(pos);
> +		kfree(slave_image);
> +	}
> +
> +	/* resources are stored in link list */
> +	list_for_each(pos, &(tsi148_bridge->master_resources)) {
> +		master_image = list_entry(pos, struct vme_master_resource,				list);
> +		list_del(pos);
> +		kfree(master_image);
> +	}
> +
> +	tsi148_irq_exit(pdev);
> +
> +	iounmap(tsi148_bridge->base);
> +
> +	pci_release_regions(pdev);
> +
> +	pci_disable_device(pdev);
> +
> +	kfree(tsi148_bridge);
> +}
> +
> +static void __exit tsi148_exit(void)
> +{
> +	pci_unregister_driver(&tsi148_driver);
> +
> +	printk(KERN_DEBUG "Driver removed.\n");
> +}
> +
> +MODULE_PARM_DESC(err_chk, "Check for VME errors on reads and writes");
> +module_param(err_chk, bool, 0);
> +
> +MODULE_DESCRIPTION("VME driver for the Tundra Tempe VME bridge");
> +MODULE_LICENSE("GPL");
> +
> +module_init(tsi148_init);
> +module_exit(tsi148_exit);
> +
> +/*----------------------------------------------------------------------------
> + * STAGING
> + *--------------------------------------------------------------------------*/
> +
> +#if 0
> +/*
> + * Direct Mode DMA transfer
> + *
> + * XXX Not looking at direct mode for now, we can always use link list mode
> + *     with a single entry.
> + */
> +int tsi148_dma_run(struct vme_dma_resource *resource, struct vme_dma_attr src,
> +	struct vme_dma_attr dest, size_t count)
> +{
> +	u32 dctlreg = 0;
> +	unsigned int tmp;
> +	int val;
> +	int channel, x;
> +	struct vmeDmaPacket *cur_dma;
> +	struct tsi148_dma_descriptor *dmaLL;
> +
> +	/* direct mode */
> +	dctlreg = 0x800000;
> +
> +	for (x = 0; x < 8; x++) {	/* vme block size */
> +		if ((32 << x) >= vmeDma->maxVmeBlockSize) {
> +			break;
> +		}
> +	}
> +	if (x == 8)
> +		x = 7;
> +	dctlreg |= (x << 12);
> +
> +	for (x = 0; x < 8; x++) {	/* pci block size */
> +		if ((32 << x) >= vmeDma->maxPciBlockSize) {
> +			break;
> +		}
> +	}
> +	if (x == 8)
> +		x = 7;
> +	dctlreg |= (x << 4);
> +
> +	if (vmeDma->vmeBackOffTimer) {
> +		for (x = 1; x < 8; x++) {	/* vme timer */
> +			if ((1 << (x - 1)) >= vmeDma->vmeBackOffTimer) {
> +				break;
> +			}
> +		}
> +		if (x == 8)
> +			x = 7;
> +		dctlreg |= (x << 8);
> +	}
> +
> +	if (vmeDma->pciBackOffTimer) {
> +		for (x = 1; x < 8; x++) {	/* pci timer */
> +			if ((1 << (x - 1)) >= vmeDma->pciBackOffTimer) {
> +				break;
> +			}
> +		}
> +		if (x == 8)
> +			x = 7;
> +		dctlreg |= (x << 0);
> +	}
> +
> +	/* Program registers for DMA transfer */
> +	iowrite32be(dmaLL->dsau, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAU);
> +	iowrite32be(dmaLL->dsal, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAL);
> +	iowrite32be(dmaLL->ddau, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAU);
> +	iowrite32be(dmaLL->ddal, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAL);
> +	iowrite32be(dmaLL->dsat, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DSAT);
> +	iowrite32be(dmaLL->ddat, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDAT);
> +	iowrite32be(dmaLL->dcnt, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCNT);
> +	iowrite32be(dmaLL->ddbs, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DDBS);
> +
> +	/* Start the operation */
> +	iowrite32be(dctlreg | 0x2000000, tsi148_bridge->base +
> +		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
> +
> +	tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
> +		TSI148_LCSR_OFFSET_DSTA);
> +	wait_event_interruptible(dma_queue[channel], (tmp & 0x1000000) == 0);
> +
> +	/*
> +	 * Read status register, we should probably do this in some error
> +	 * handler rather than here so that we can be sure we haven't kicked off
> +	 * another DMA transfer.
> +	 */
> +	val = ioread32be(tsi148_bridge->base + TSI148_LCSR_DMA[channel] +
> +		TSI148_LCSR_OFFSET_DSTA);
> +
> +	vmeDma->vmeDmaStatus = 0;
> +	if (val & 0x10000000) {
> +		printk(KERN_ERR
> +			"DMA Error in DMA_tempe_irqhandler DSTA=%08X\n",
> +			val);
> +		vmeDma->vmeDmaStatus = val;
> +
> +	}
> +	return (0);
> +}
> +#endif
> +
> +#if 0
> +
> +/* Global VME controller information */
> +struct pci_dev *vme_pci_dev;
> +
> +/*
> + * Set the VME bus arbiter with the requested attributes
> + */
> +int tempe_set_arbiter(vmeArbiterCfg_t * vmeArb)
> +{
> +	int temp_ctl = 0;
> +	int gto = 0;
> +
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VCTRL);
> +	temp_ctl &= 0xFFEFFF00;
> +
> +	if (vmeArb->globalTimeoutTimer == 0xFFFFFFFF) {
> +		gto = 8;
> +	} else if (vmeArb->globalTimeoutTimer > 2048) {
> +		return (-EINVAL);
> +	} else if (vmeArb->globalTimeoutTimer == 0) {
> +		gto = 0;
> +	} else {
> +		gto = 1;
> +		while ((16 * (1 << (gto - 1))) < vmeArb->globalTimeoutTimer) {
> +			gto += 1;
> +		}
> +	}
> +	temp_ctl |= gto;
> +
> +	if (vmeArb->arbiterMode != VME_PRIORITY_MODE) {
> +		temp_ctl |= 1 << 6;
> +	}
> +
> +	if (vmeArb->arbiterTimeoutFlag) {
> +		temp_ctl |= 1 << 7;
> +	}
> +
> +	if (vmeArb->noEarlyReleaseFlag) {
> +		temp_ctl |= 1 << 20;
> +	}
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_VCTRL);
> +
> +	return (0);
> +}
> +
> +/*
> + * Return the attributes of the VME bus arbiter.
> + */
> +int tempe_get_arbiter(vmeArbiterCfg_t * vmeArb)
> +{
> +	int temp_ctl = 0;
> +	int gto = 0;
> +
> +
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VCTRL);
> +
> +	gto = temp_ctl & 0xF;
> +	if (gto != 0) {
> +		vmeArb->globalTimeoutTimer = (16 * (1 << (gto - 1)));
> +	}
> +
> +	if (temp_ctl & (1 << 6)) {
> +		vmeArb->arbiterMode = VME_R_ROBIN_MODE;
> +	} else {
> +		vmeArb->arbiterMode = VME_PRIORITY_MODE;
> +	}
> +
> +	if (temp_ctl & (1 << 7)) {
> +		vmeArb->arbiterTimeoutFlag = 1;
> +	}
> +
> +	if (temp_ctl & (1 << 20)) {
> +		vmeArb->noEarlyReleaseFlag = 1;
> +	}
> +
> +	return (0);
> +}
> +
> +/*
> + * Set the VME bus requestor with the requested attributes
> + */
> +int tempe_set_requestor(vmeRequesterCfg_t * vmeReq)
> +{
> +	int temp_ctl = 0;
> +
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +	temp_ctl &= 0xFFFF0000;
> +
> +	if (vmeReq->releaseMode == 1) {
> +		temp_ctl |= (1 << 3);
> +	}
> +
> +	if (vmeReq->fairMode == 1) {
> +		temp_ctl |= (1 << 2);
> +	}
> +
> +	temp_ctl |= (vmeReq->timeonTimeoutTimer & 7) << 8;
> +	temp_ctl |= (vmeReq->timeoffTimeoutTimer & 7) << 12;
> +	temp_ctl |= vmeReq->requestLevel;
> +
> +	iowrite32be(temp_ctl, tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +	return (0);
> +}
> +
> +/*
> + * Return the attributes of the VME bus requestor
> + */
> +int tempe_get_requestor(vmeRequesterCfg_t * vmeReq)
> +{
> +	int temp_ctl = 0;
> +
> +	temp_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_VMCTRL);
> +
> +	if (temp_ctl & 0x18) {
> +		vmeReq->releaseMode = 1;
> +	}
> +
> +	if (temp_ctl & (1 << 2)) {
> +		vmeReq->fairMode = 1;
> +	}
> +
> +	vmeReq->requestLevel = temp_ctl & 3;
> +	vmeReq->timeonTimeoutTimer = (temp_ctl >> 8) & 7;
> +	vmeReq->timeoffTimeoutTimer = (temp_ctl >> 12) & 7;
> +
> +	return (0);
> +}
> +
> +
> +#endif
> --- /dev/null
> +++ b/drivers/staging/vme/bridges/vme_tsi148.h
> @@ -0,0 +1,1387 @@
> +/*
> + * tsi148.h
> + *
> + * Support for the Tundra TSI148 VME Bridge chip
> + *
> + * Author: Tom Armistead
> + * Updated and maintained by Ajit Prem
> + * Copyright 2004 Motorola Inc.
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef TSI148_H
> +#define TSI148_H
> +
> +#ifndef	PCI_VENDOR_ID_TUNDRA
> +#define	PCI_VENDOR_ID_TUNDRA 0x10e3
> +#endif
> +
> +#ifndef	PCI_DEVICE_ID_TUNDRA_TSI148
> +#define	PCI_DEVICE_ID_TUNDRA_TSI148 0x148
> +#endif
> +
> +/*
> + *  Define the number of each that the Tsi148 supports.
> + */
> +#define TSI148_MAX_MASTER		8	/* Max Master Windows */
> +#define TSI148_MAX_SLAVE		8	/* Max Slave Windows */
> +#define TSI148_MAX_DMA			2	/* Max DMA Controllers */
> +#define TSI148_MAX_MAILBOX		4	/* Max Mail Box registers */
> +#define TSI148_MAX_SEMAPHORE		8	/* Max Semaphores */
> +
> +/*
> + * Layout of a DMAC Linked-List Descriptor
> + *
> + * Note: This structure is accessed via the chip and therefore must be
> + *       correctly laid out - It must also be aligned on 64-bit boundaries.
> + */
> +struct tsi148_dma_descriptor {
> +	u32 dsau;      /* Source Address */
> +	u32 dsal;
> +	u32 ddau;      /* Destination Address */
> +	u32 ddal;
> +	u32 dsat;      /* Source attributes */
> +	u32 ddat;      /* Destination attributes */
> +	u32 dnlau;     /* Next link address */
> +	u32 dnlal;
> +	u32 dcnt;      /* Byte count */
> +	u32 ddbs;      /* 2eSST Broadcast select */
> +};
> +
> +struct tsi148_dma_entry {
> +	/*
> +	 * The descriptor needs to be aligned on a 64-bit boundary, we increase
> +	 * the chance of this by putting it first in the structure.
> +	 */
> +	struct tsi148_dma_descriptor descriptor;
> +	struct list_head list;
> +};
> +
> +/*
> + *  TSI148 ASIC register structure overlays and bit field definitions.
> + *
> + *      Note:   Tsi148 Register Group (CRG) consists of the following
> + *              combination of registers:
> + *                      PCFS    - PCI Configuration Space Registers
> + *                      LCSR    - Local Control and Status Registers
> + *                      GCSR    - Global Control and Status Registers
> + *                      CR/CSR  - Subset of Configuration ROM /
> + *                                Control and Status Registers
> + */
> +
> +
> +/*
> + *  Command/Status Registers (CRG + $004)
> + */
> +#define TSI148_PCFS_ID			0x0
> +#define TSI148_PCFS_CSR			0x4
> +#define TSI148_PCFS_CLASS		0x8
> +#define TSI148_PCFS_MISC0		0xC
> +#define TSI148_PCFS_MBARL		0x10
> +#define TSI148_PCFS_MBARU		0x14
> +
> +#define TSI148_PCFS_SUBID		0x28
> +
> +#define TSI148_PCFS_CAPP		0x34
> +
> +#define TSI148_PCFS_MISC1		0x3C
> +
> +#define TSI148_PCFS_XCAPP		0x40
> +#define TSI148_PCFS_XSTAT		0x44
> +
> +/*
> + * LCSR definitions
> + */
> +
> +/*
> + *    Outbound Translations
> + */
> +#define TSI148_LCSR_OT0_OTSAU		0x100
> +#define TSI148_LCSR_OT0_OTSAL		0x104
> +#define TSI148_LCSR_OT0_OTEAU		0x108
> +#define TSI148_LCSR_OT0_OTEAL		0x10C
> +#define TSI148_LCSR_OT0_OTOFU		0x110
> +#define TSI148_LCSR_OT0_OTOFL		0x114
> +#define TSI148_LCSR_OT0_OTBS		0x118
> +#define TSI148_LCSR_OT0_OTAT		0x11C
> +
> +#define TSI148_LCSR_OT1_OTSAU		0x120
> +#define TSI148_LCSR_OT1_OTSAL		0x124
> +#define TSI148_LCSR_OT1_OTEAU		0x128
> +#define TSI148_LCSR_OT1_OTEAL		0x12C
> +#define TSI148_LCSR_OT1_OTOFU		0x130
> +#define TSI148_LCSR_OT1_OTOFL		0x134
> +#define TSI148_LCSR_OT1_OTBS		0x138
> +#define TSI148_LCSR_OT1_OTAT		0x13C
> +
> +#define TSI148_LCSR_OT2_OTSAU		0x140
> +#define TSI148_LCSR_OT2_OTSAL		0x144
> +#define TSI148_LCSR_OT2_OTEAU		0x148
> +#define TSI148_LCSR_OT2_OTEAL		0x14C
> +#define TSI148_LCSR_OT2_OTOFU		0x150
> +#define TSI148_LCSR_OT2_OTOFL		0x154
> +#define TSI148_LCSR_OT2_OTBS		0x158
> +#define TSI148_LCSR_OT2_OTAT		0x15C
> +
> +#define TSI148_LCSR_OT3_OTSAU		0x160
> +#define TSI148_LCSR_OT3_OTSAL		0x164
> +#define TSI148_LCSR_OT3_OTEAU		0x168
> +#define TSI148_LCSR_OT3_OTEAL		0x16C
> +#define TSI148_LCSR_OT3_OTOFU		0x170
> +#define TSI148_LCSR_OT3_OTOFL		0x174
> +#define TSI148_LCSR_OT3_OTBS		0x178
> +#define TSI148_LCSR_OT3_OTAT		0x17C
> +
> +#define TSI148_LCSR_OT4_OTSAU		0x180
> +#define TSI148_LCSR_OT4_OTSAL		0x184
> +#define TSI148_LCSR_OT4_OTEAU		0x188
> +#define TSI148_LCSR_OT4_OTEAL		0x18C
> +#define TSI148_LCSR_OT4_OTOFU		0x190
> +#define TSI148_LCSR_OT4_OTOFL		0x194
> +#define TSI148_LCSR_OT4_OTBS		0x198
> +#define TSI148_LCSR_OT4_OTAT		0x19C
> +
> +#define TSI148_LCSR_OT5_OTSAU		0x1A0
> +#define TSI148_LCSR_OT5_OTSAL		0x1A4
> +#define TSI148_LCSR_OT5_OTEAU		0x1A8
> +#define TSI148_LCSR_OT5_OTEAL		0x1AC
> +#define TSI148_LCSR_OT5_OTOFU		0x1B0
> +#define TSI148_LCSR_OT5_OTOFL		0x1B4
> +#define TSI148_LCSR_OT5_OTBS		0x1B8
> +#define TSI148_LCSR_OT5_OTAT		0x1BC
> +
> +#define TSI148_LCSR_OT6_OTSAU		0x1C0
> +#define TSI148_LCSR_OT6_OTSAL		0x1C4
> +#define TSI148_LCSR_OT6_OTEAU		0x1C8
> +#define TSI148_LCSR_OT6_OTEAL		0x1CC
> +#define TSI148_LCSR_OT6_OTOFU		0x1D0
> +#define TSI148_LCSR_OT6_OTOFL		0x1D4
> +#define TSI148_LCSR_OT6_OTBS		0x1D8
> +#define TSI148_LCSR_OT6_OTAT		0x1DC
> +
> +#define TSI148_LCSR_OT7_OTSAU		0x1E0
> +#define TSI148_LCSR_OT7_OTSAL		0x1E4
> +#define TSI148_LCSR_OT7_OTEAU		0x1E8
> +#define TSI148_LCSR_OT7_OTEAL		0x1EC
> +#define TSI148_LCSR_OT7_OTOFU		0x1F0
> +#define TSI148_LCSR_OT7_OTOFL		0x1F4
> +#define TSI148_LCSR_OT7_OTBS		0x1F8
> +#define TSI148_LCSR_OT7_OTAT		0x1FC
> +
> +#define TSI148_LCSR_OT0		0x100
> +#define TSI148_LCSR_OT1		0x120
> +#define TSI148_LCSR_OT2		0x140
> +#define TSI148_LCSR_OT3		0x160
> +#define TSI148_LCSR_OT4		0x180
> +#define TSI148_LCSR_OT5		0x1A0
> +#define TSI148_LCSR_OT6		0x1C0
> +#define TSI148_LCSR_OT7		0x1E0
> +
> +static const int TSI148_LCSR_OT[8] = { TSI148_LCSR_OT0, TSI148_LCSR_OT1,
> +					 TSI148_LCSR_OT2, TSI148_LCSR_OT3,
> +					 TSI148_LCSR_OT4, TSI148_LCSR_OT5,
> +					 TSI148_LCSR_OT6, TSI148_LCSR_OT7 };
> +
> +#define TSI148_LCSR_OFFSET_OTSAU	0x0
> +#define TSI148_LCSR_OFFSET_OTSAL	0x4
> +#define TSI148_LCSR_OFFSET_OTEAU	0x8
> +#define TSI148_LCSR_OFFSET_OTEAL	0xC
> +#define TSI148_LCSR_OFFSET_OTOFU	0x10
> +#define TSI148_LCSR_OFFSET_OTOFL	0x14
> +#define TSI148_LCSR_OFFSET_OTBS		0x18
> +#define TSI148_LCSR_OFFSET_OTAT		0x1C
> +
> +/*
> + * VMEbus interupt ack
> + * offset  200
> + */
> +#define TSI148_LCSR_VIACK1	0x204
> +#define TSI148_LCSR_VIACK2	0x208
> +#define TSI148_LCSR_VIACK3	0x20C
> +#define TSI148_LCSR_VIACK4	0x210
> +#define TSI148_LCSR_VIACK5	0x214
> +#define TSI148_LCSR_VIACK6	0x218
> +#define TSI148_LCSR_VIACK7	0x21C
> +
> +static const int TSI148_LCSR_VIACK[8] = { 0, TSI148_LCSR_VIACK1,
> +				TSI148_LCSR_VIACK2, TSI148_LCSR_VIACK3,
> +				TSI148_LCSR_VIACK4, TSI148_LCSR_VIACK5,
> +				TSI148_LCSR_VIACK6, TSI148_LCSR_VIACK7 };
> +
> +/*
> + * RMW
> + * offset    220
> + */
> +#define TSI148_LCSR_RMWAU	0x220
> +#define TSI148_LCSR_RMWAL	0x224
> +#define TSI148_LCSR_RMWEN	0x228
> +#define TSI148_LCSR_RMWC	0x22C
> +#define TSI148_LCSR_RMWS	0x230
> +
> +/*
> + * VMEbus control
> + * offset    234
> + */
> +#define TSI148_LCSR_VMCTRL	0x234
> +#define TSI148_LCSR_VCTRL	0x238
> +#define TSI148_LCSR_VSTAT	0x23C
> +
> +/*
> + * PCI status
> + * offset  240
> + */
> +#define TSI148_LCSR_PSTAT	0x240
> +
> +/*
> + * VME filter.
> + * offset  250
> + */
> +#define TSI148_LCSR_VMEFL	0x250
> +
> +	/*
> +	 * VME exception.
> +	 * offset  260
> + */
> +#define TSI148_LCSR_VEAU	0x260
> +#define TSI148_LCSR_VEAL	0x264
> +#define TSI148_LCSR_VEAT	0x268
> +
> +	/*
> +	 * PCI error
> +	 * offset  270
> +	 */
> +#define TSI148_LCSR_EDPAU	0x270
> +#define TSI148_LCSR_EDPAL	0x274
> +#define TSI148_LCSR_EDPXA	0x278
> +#define TSI148_LCSR_EDPXS	0x27C
> +#define TSI148_LCSR_EDPAT	0x280
> +
> +	/*
> +	 * Inbound Translations
> +	 * offset  300
> +	 */
> +#define TSI148_LCSR_IT0_ITSAU		0x300
> +#define TSI148_LCSR_IT0_ITSAL		0x304
> +#define TSI148_LCSR_IT0_ITEAU		0x308
> +#define TSI148_LCSR_IT0_ITEAL		0x30C
> +#define TSI148_LCSR_IT0_ITOFU		0x310
> +#define TSI148_LCSR_IT0_ITOFL		0x314
> +#define TSI148_LCSR_IT0_ITAT		0x318
> +
> +#define TSI148_LCSR_IT1_ITSAU		0x320
> +#define TSI148_LCSR_IT1_ITSAL		0x324
> +#define TSI148_LCSR_IT1_ITEAU		0x328
> +#define TSI148_LCSR_IT1_ITEAL		0x32C
> +#define TSI148_LCSR_IT1_ITOFU		0x330
> +#define TSI148_LCSR_IT1_ITOFL		0x334
> +#define TSI148_LCSR_IT1_ITAT		0x338
> +
> +#define TSI148_LCSR_IT2_ITSAU		0x340
> +#define TSI148_LCSR_IT2_ITSAL		0x344
> +#define TSI148_LCSR_IT2_ITEAU		0x348
> +#define TSI148_LCSR_IT2_ITEAL		0x34C
> +#define TSI148_LCSR_IT2_ITOFU		0x350
> +#define TSI148_LCSR_IT2_ITOFL		0x354
> +#define TSI148_LCSR_IT2_ITAT		0x358
> +
> +#define TSI148_LCSR_IT3_ITSAU		0x360
> +#define TSI148_LCSR_IT3_ITSAL		0x364
> +#define TSI148_LCSR_IT3_ITEAU		0x368
> +#define TSI148_LCSR_IT3_ITEAL		0x36C
> +#define TSI148_LCSR_IT3_ITOFU		0x370
> +#define TSI148_LCSR_IT3_ITOFL		0x374
> +#define TSI148_LCSR_IT3_ITAT		0x378
> +
> +#define TSI148_LCSR_IT4_ITSAU		0x380
> +#define TSI148_LCSR_IT4_ITSAL		0x384
> +#define TSI148_LCSR_IT4_ITEAU		0x388
> +#define TSI148_LCSR_IT4_ITEAL		0x38C
> +#define TSI148_LCSR_IT4_ITOFU		0x390
> +#define TSI148_LCSR_IT4_ITOFL		0x394
> +#define TSI148_LCSR_IT4_ITAT		0x398
> +
> +#define TSI148_LCSR_IT5_ITSAU		0x3A0
> +#define TSI148_LCSR_IT5_ITSAL		0x3A4
> +#define TSI148_LCSR_IT5_ITEAU		0x3A8
> +#define TSI148_LCSR_IT5_ITEAL		0x3AC
> +#define TSI148_LCSR_IT5_ITOFU		0x3B0
> +#define TSI148_LCSR_IT5_ITOFL		0x3B4
> +#define TSI148_LCSR_IT5_ITAT		0x3B8
> +
> +#define TSI148_LCSR_IT6_ITSAU		0x3C0
> +#define TSI148_LCSR_IT6_ITSAL		0x3C4
> +#define TSI148_LCSR_IT6_ITEAU		0x3C8
> +#define TSI148_LCSR_IT6_ITEAL		0x3CC
> +#define TSI148_LCSR_IT6_ITOFU		0x3D0
> +#define TSI148_LCSR_IT6_ITOFL		0x3D4
> +#define TSI148_LCSR_IT6_ITAT		0x3D8
> +
> +#define TSI148_LCSR_IT7_ITSAU		0x3E0
> +#define TSI148_LCSR_IT7_ITSAL		0x3E4
> +#define TSI148_LCSR_IT7_ITEAU		0x3E8
> +#define TSI148_LCSR_IT7_ITEAL		0x3EC
> +#define TSI148_LCSR_IT7_ITOFU		0x3F0
> +#define TSI148_LCSR_IT7_ITOFL		0x3F4
> +#define TSI148_LCSR_IT7_ITAT		0x3F8
> +
> +
> +#define TSI148_LCSR_IT0		0x300
> +#define TSI148_LCSR_IT1		0x320
> +#define TSI148_LCSR_IT2		0x340
> +#define TSI148_LCSR_IT3		0x360
> +#define TSI148_LCSR_IT4		0x380
> +#define TSI148_LCSR_IT5		0x3A0
> +#define TSI148_LCSR_IT6		0x3C0
> +#define TSI148_LCSR_IT7		0x3E0
> +
> +static const int TSI148_LCSR_IT[8] = { TSI148_LCSR_IT0, TSI148_LCSR_IT1,
> +					 TSI148_LCSR_IT2, TSI148_LCSR_IT3,
> +					 TSI148_LCSR_IT4, TSI148_LCSR_IT5,
> +					 TSI148_LCSR_IT6, TSI148_LCSR_IT7 };
> +
> +#define TSI148_LCSR_OFFSET_ITSAU	0x0
> +#define TSI148_LCSR_OFFSET_ITSAL	0x4
> +#define TSI148_LCSR_OFFSET_ITEAU	0x8
> +#define TSI148_LCSR_OFFSET_ITEAL	0xC
> +#define TSI148_LCSR_OFFSET_ITOFU	0x10
> +#define TSI148_LCSR_OFFSET_ITOFL	0x14
> +#define TSI148_LCSR_OFFSET_ITAT		0x18
> +
> +	/*
> +	 * Inbound Translation GCSR
> +	 * offset  400
> +	 */
> +#define TSI148_LCSR_GBAU	0x400
> +#define TSI148_LCSR_GBAL	0x404
> +#define TSI148_LCSR_GCSRAT	0x408
> +
> +	/*
> +	 * Inbound Translation CRG
> +	 * offset  40C
> +	 */
> +#define TSI148_LCSR_CBAU	0x40C
> +#define TSI148_LCSR_CBAL	0x410
> +#define TSI148_LCSR_CSRAT	0x414
> +
> +	/*
> +	 * Inbound Translation CR/CSR
> +	 *         CRG
> +	 * offset  418
> +	 */
> +#define TSI148_LCSR_CROU	0x418
> +#define TSI148_LCSR_CROL	0x41C
> +#define TSI148_LCSR_CRAT	0x420
> +
> +	/*
> +	 * Inbound Translation Location Monitor
> +	 * offset  424
> +	 */
> +#define TSI148_LCSR_LMBAU	0x424
> +#define TSI148_LCSR_LMBAL	0x428
> +#define TSI148_LCSR_LMAT	0x42C
> +
> +	/*
> +	 * VMEbus Interrupt Control.
> +	 * offset  430
> +	 */
> +#define TSI148_LCSR_BCU		0x430
> +#define TSI148_LCSR_BCL		0x434
> +#define TSI148_LCSR_BPGTR	0x438
> +#define TSI148_LCSR_BPCTR	0x43C
> +#define TSI148_LCSR_VICR	0x440
> +
> +	/*
> +	 * Local Bus Interrupt Control.
> +	 * offset  448
> +	 */
> +#define TSI148_LCSR_INTEN	0x448
> +#define TSI148_LCSR_INTEO	0x44C
> +#define TSI148_LCSR_INTS	0x450
> +#define TSI148_LCSR_INTC	0x454
> +#define TSI148_LCSR_INTM1	0x458
> +#define TSI148_LCSR_INTM2	0x45C
> +
> +	/*
> +	 * DMA Controllers
> +	 * offset 500
> +	 */
> +#define TSI148_LCSR_DCTL0	0x500
> +#define TSI148_LCSR_DSTA0	0x504
> +#define TSI148_LCSR_DCSAU0	0x508
> +#define TSI148_LCSR_DCSAL0	0x50C
> +#define TSI148_LCSR_DCDAU0	0x510
> +#define TSI148_LCSR_DCDAL0	0x514
> +#define TSI148_LCSR_DCLAU0	0x518
> +#define TSI148_LCSR_DCLAL0	0x51C
> +#define TSI148_LCSR_DSAU0	0x520
> +#define TSI148_LCSR_DSAL0	0x524
> +#define TSI148_LCSR_DDAU0	0x528
> +#define TSI148_LCSR_DDAL0	0x52C
> +#define TSI148_LCSR_DSAT0	0x530
> +#define TSI148_LCSR_DDAT0	0x534
> +#define TSI148_LCSR_DNLAU0	0x538
> +#define TSI148_LCSR_DNLAL0	0x53C
> +#define TSI148_LCSR_DCNT0	0x540
> +#define TSI148_LCSR_DDBS0	0x544
> +
> +#define TSI148_LCSR_DCTL1	0x580
> +#define TSI148_LCSR_DSTA1	0x584
> +#define TSI148_LCSR_DCSAU1	0x588
> +#define TSI148_LCSR_DCSAL1	0x58C
> +#define TSI148_LCSR_DCDAU1	0x590
> +#define TSI148_LCSR_DCDAL1	0x594
> +#define TSI148_LCSR_DCLAU1	0x598
> +#define TSI148_LCSR_DCLAL1	0x59C
> +#define TSI148_LCSR_DSAU1	0x5A0
> +#define TSI148_LCSR_DSAL1	0x5A4
> +#define TSI148_LCSR_DDAU1	0x5A8
> +#define TSI148_LCSR_DDAL1	0x5AC
> +#define TSI148_LCSR_DSAT1	0x5B0
> +#define TSI148_LCSR_DDAT1	0x5B4
> +#define TSI148_LCSR_DNLAU1	0x5B8
> +#define TSI148_LCSR_DNLAL1	0x5BC
> +#define TSI148_LCSR_DCNT1	0x5C0
> +#define TSI148_LCSR_DDBS1	0x5C4
> +
> +#define TSI148_LCSR_DMA0	0x500
> +#define TSI148_LCSR_DMA1	0x580
> +
> +
> +static const int TSI148_LCSR_DMA[TSI148_MAX_DMA] = { TSI148_LCSR_DMA0,
> +						TSI148_LCSR_DMA1 };
> +
> +#define TSI148_LCSR_OFFSET_DCTL		0x0
> +#define TSI148_LCSR_OFFSET_DSTA		0x4
> +#define TSI148_LCSR_OFFSET_DCSAU	0x8
> +#define TSI148_LCSR_OFFSET_DCSAL	0xC
> +#define TSI148_LCSR_OFFSET_DCDAU	0x10
> +#define TSI148_LCSR_OFFSET_DCDAL	0x14
> +#define TSI148_LCSR_OFFSET_DCLAU	0x18
> +#define TSI148_LCSR_OFFSET_DCLAL	0x1C
> +#define TSI148_LCSR_OFFSET_DSAU		0x20
> +#define TSI148_LCSR_OFFSET_DSAL		0x24
> +#define TSI148_LCSR_OFFSET_DDAU		0x28
> +#define TSI148_LCSR_OFFSET_DDAL		0x2C
> +#define TSI148_LCSR_OFFSET_DSAT		0x30
> +#define TSI148_LCSR_OFFSET_DDAT		0x34
> +#define TSI148_LCSR_OFFSET_DNLAU	0x38
> +#define TSI148_LCSR_OFFSET_DNLAL	0x3C
> +#define TSI148_LCSR_OFFSET_DCNT		0x40
> +#define TSI148_LCSR_OFFSET_DDBS		0x44
> +
> +	/*
> +	 * GCSR Register Group
> +	 */
> +
> +	/*
> +	 *         GCSR    CRG
> +	 * offset   00     600 - DEVI/VENI
> +	 * offset   04     604 - CTRL/GA/REVID
> +	 * offset   08     608 - Semaphore3/2/1/0
> +	 * offset   0C     60C - Seamphore7/6/5/4
> +	 */
> +#define TSI148_GCSR_ID		0x600
> +#define TSI148_GCSR_CSR		0x604
> +#define TSI148_GCSR_SEMA0	0x608
> +#define TSI148_GCSR_SEMA1	0x60C
> +
> +	/*
> +	 * Mail Box
> +	 *         GCSR    CRG
> +	 * offset   10     610 - Mailbox0
> +	 */
> +#define TSI148_GCSR_MBOX0	0x610
> +#define TSI148_GCSR_MBOX1	0x614
> +#define TSI148_GCSR_MBOX2	0x618
> +#define TSI148_GCSR_MBOX3	0x61C
> +
> +static const int TSI148_GCSR_MBOX[4] = { TSI148_GCSR_MBOX0,
> +					TSI148_GCSR_MBOX1,
> +					TSI148_GCSR_MBOX2,
> +					TSI148_GCSR_MBOX3 };
> +
> +	/*
> +	 * CR/CSR
> +	 */
> +
> +	/*
> +	 *        CR/CSR   CRG
> +	 * offset  7FFF4   FF4 - CSRBCR
> +	 * offset  7FFF8   FF8 - CSRBSR
> +	 * offset  7FFFC   FFC - CBAR
> +	 */
> +#define TSI148_CSRBCR	0xFF4
> +#define TSI148_CSRBSR	0xFF8
> +#define TSI148_CBAR	0xFFC
> +
> +
> +
> +
> +	/*
> +	 *  TSI148 Register Bit Definitions
> +	 */
> +
> +	/*
> +	 *  PFCS Register Set
> +	 */
> +#define TSI148_PCFS_CMMD_SERR          (1<<8)	/* SERR_L out pin ssys err */
> +#define TSI148_PCFS_CMMD_PERR          (1<<6)	/* PERR_L out pin  parity */
> +#define TSI148_PCFS_CMMD_MSTR          (1<<2)	/* PCI bus master */
> +#define TSI148_PCFS_CMMD_MEMSP         (1<<1)	/* PCI mem space access  */
> +#define TSI148_PCFS_CMMD_IOSP          (1<<0)	/* PCI I/O space enable */
> +
> +#define TSI148_PCFS_STAT_RCPVE         (1<<15)	/* Detected Parity Error */
> +#define TSI148_PCFS_STAT_SIGSE         (1<<14)	/* Signalled System Error */
> +#define TSI148_PCFS_STAT_RCVMA         (1<<13)	/* Received Master Abort */
> +#define TSI148_PCFS_STAT_RCVTA         (1<<12)	/* Received Target Abort */
> +#define TSI148_PCFS_STAT_SIGTA         (1<<11)	/* Signalled Target Abort */
> +#define TSI148_PCFS_STAT_SELTIM        (3<<9)	/* DELSEL Timing */
> +#define TSI148_PCFS_STAT_DPAR          (1<<8)	/* Data Parity Err Reported */
> +#define TSI148_PCFS_STAT_FAST          (1<<7)	/* Fast back-to-back Cap */
> +#define TSI148_PCFS_STAT_P66M          (1<<5)	/* 66 MHz Capable */
> +#define TSI148_PCFS_STAT_CAPL          (1<<4)	/* Capab List - address $34 */
> +
> +/*
> + *  Revision ID/Class Code Registers   (CRG +$008)
> + */
> +#define TSI148_PCFS_CLAS_M             (0xFF<<24)	/* Class ID */
> +#define TSI148_PCFS_SUBCLAS_M          (0xFF<<16)	/* Sub-Class ID */
> +#define TSI148_PCFS_PROGIF_M           (0xFF<<8)	/* Sub-Class ID */
> +#define TSI148_PCFS_REVID_M            (0xFF<<0)	/* Rev ID */
> +
> +/*
> + * Cache Line Size/ Master Latency Timer/ Header Type Registers (CRG + $00C)
> + */
> +#define TSI148_PCFS_HEAD_M             (0xFF<<16)	/* Master Lat Timer */
> +#define TSI148_PCFS_MLAT_M             (0xFF<<8)	/* Master Lat Timer */
> +#define TSI148_PCFS_CLSZ_M             (0xFF<<0)	/* Cache Line Size */
> +
> +/*
> + *  Memory Base Address Lower Reg (CRG + $010)
> + */
> +#define TSI148_PCFS_MBARL_BASEL_M      (0xFFFFF<<12)	/* Base Addr Lower Mask */
> +#define TSI148_PCFS_MBARL_PRE          (1<<3)	/* Prefetch */
> +#define TSI148_PCFS_MBARL_MTYPE_M      (3<<1)	/* Memory Type Mask */
> +#define TSI148_PCFS_MBARL_IOMEM        (1<<0)	/* I/O Space Indicator */
> +
> +/*
> + *  Message Signaled Interrupt Capabilities Register (CRG + $040)
> + */
> +#define TSI148_PCFS_MSICAP_64BAC       (1<<7)	/* 64-bit Address Capable */
> +#define TSI148_PCFS_MSICAP_MME_M       (7<<4)	/* Multiple Msg Enable Mask */
> +#define TSI148_PCFS_MSICAP_MMC_M       (7<<1)	/* Multiple Msg Capable Mask */
> +#define TSI148_PCFS_MSICAP_MSIEN       (1<<0)	/* Msg signaled INT Enable */
> +
> +/*
> + *  Message Address Lower Register (CRG +$044)
> + */
> +#define TSI148_PCFS_MSIAL_M            (0x3FFFFFFF<<2)	/* Mask */
> +
> +/*
> + *  Message Data Register (CRG + 4C)
> + */
> +#define TSI148_PCFS_MSIMD_M            (0xFFFF<<0)	/* Mask */
> +
> +/*
> + *  PCI-X Capabilities Register (CRG + $050)
> + */
> +#define TSI148_PCFS_PCIXCAP_MOST_M     (7<<4)	/* Max outstanding Split Tran */
> +#define TSI148_PCFS_PCIXCAP_MMRBC_M    (3<<2)	/* Max Mem Read byte cnt */
> +#define TSI148_PCFS_PCIXCAP_ERO        (1<<1)	/* Enable Relaxed Ordering */
> +#define TSI148_PCFS_PCIXCAP_DPERE      (1<<0)	/* Data Parity Recover Enable */
> +
> +/*
> + *  PCI-X Status Register (CRG +$054)
> + */
> +#define TSI148_PCFS_PCIXSTAT_RSCEM     (1<<29)	/* Recieved Split Comp Error */
> +#define TSI148_PCFS_PCIXSTAT_DMCRS_M   (7<<26)	/* max Cumulative Read Size */
> +#define TSI148_PCFS_PCIXSTAT_DMOST_M   (7<<23)	/* max outstanding Split Trans */
> +#define TSI148_PCFS_PCIXSTAT_DMMRC_M   (3<<21)	/* max mem read byte count */
> +#define TSI148_PCFS_PCIXSTAT_DC        (1<<20)	/* Device Complexity */
> +#define TSI148_PCFS_PCIXSTAT_USC       (1<<19)	/* Unexpected Split comp */
> +#define TSI148_PCFS_PCIXSTAT_SCD       (1<<18)	/* Split completion discard */
> +#define TSI148_PCFS_PCIXSTAT_133C      (1<<17)	/* 133MHz capable */
> +#define TSI148_PCFS_PCIXSTAT_64D       (1<<16)	/* 64 bit device */
> +#define TSI148_PCFS_PCIXSTAT_BN_M      (0xFF<<8)	/* Bus number */
> +#define TSI148_PCFS_PCIXSTAT_DN_M      (0x1F<<3)	/* Device number */
> +#define TSI148_PCFS_PCIXSTAT_FN_M      (7<<0)	/* Function Number */
> +
> +/*
> + *  LCSR Registers
> + */
> +
> +/*
> + *  Outbound Translation Starting Address Lower
> + */
> +#define TSI148_LCSR_OTSAL_M            (0xFFFF<<16)	/* Mask */
> +
> +/*
> + *  Outbound Translation Ending Address Lower
> + */
> +#define TSI148_LCSR_OTEAL_M            (0xFFFF<<16)	/* Mask */
> +
> +/*
> + *  Outbound Translation Offset Lower
> + */
> +#define TSI148_LCSR_OTOFFL_M           (0xFFFF<<16)	/* Mask */
> +
> +/*
> + *  Outbound Translation 2eSST Broadcast Select
> + */
> +#define TSI148_LCSR_OTBS_M             (0xFFFFF<<0)	/* Mask */
> +
> +/*
> + *  Outbound Translation Attribute
> + */
> +#define TSI148_LCSR_OTAT_EN            (1<<31)	/* Window Enable */
> +#define TSI148_LCSR_OTAT_MRPFD         (1<<18)	/* Prefetch Disable */
> +
> +#define TSI148_LCSR_OTAT_PFS_M         (3<<16)	/* Prefetch Size Mask */
> +#define TSI148_LCSR_OTAT_PFS_2         (0<<16)	/* 2 Cache Lines P Size */
> +#define TSI148_LCSR_OTAT_PFS_4         (1<<16)	/* 4 Cache Lines P Size */
> +#define TSI148_LCSR_OTAT_PFS_8         (2<<16)	/* 8 Cache Lines P Size */
> +#define TSI148_LCSR_OTAT_PFS_16        (3<<16)	/* 16 Cache Lines P Size */
> +
> +#define TSI148_LCSR_OTAT_2eSSTM_M      (7<<11)	/* 2eSST Xfer Rate Mask */
> +#define TSI148_LCSR_OTAT_2eSSTM_160    (0<<11)	/* 160MB/s 2eSST Xfer Rate */
> +#define TSI148_LCSR_OTAT_2eSSTM_267    (1<<11)	/* 267MB/s 2eSST Xfer Rate */
> +#define TSI148_LCSR_OTAT_2eSSTM_320    (2<<11)	/* 320MB/s 2eSST Xfer Rate */
> +
> +#define TSI148_LCSR_OTAT_TM_M          (7<<8)	/* Xfer Protocol Mask */
> +#define TSI148_LCSR_OTAT_TM_SCT        (0<<8)	/* SCT Xfer Protocol */
> +#define TSI148_LCSR_OTAT_TM_BLT        (1<<8)	/* BLT Xfer Protocol */
> +#define TSI148_LCSR_OTAT_TM_MBLT       (2<<8)	/* MBLT Xfer Protocol */
> +#define TSI148_LCSR_OTAT_TM_2eVME      (3<<8)	/* 2eVME Xfer Protocol */
> +#define TSI148_LCSR_OTAT_TM_2eSST      (4<<8)	/* 2eSST Xfer Protocol */
> +#define TSI148_LCSR_OTAT_TM_2eSSTB     (5<<8)	/* 2eSST Bcast Xfer Protocol */
> +
> +#define TSI148_LCSR_OTAT_DBW_M         (3<<6)	/* Max Data Width */
> +#define TSI148_LCSR_OTAT_DBW_16        (0<<6)	/* 16-bit Data Width */
> +#define TSI148_LCSR_OTAT_DBW_32        (1<<6)	/* 32-bit Data Width */
> +
> +#define TSI148_LCSR_OTAT_SUP           (1<<5)	/* Supervisory Access */
> +#define TSI148_LCSR_OTAT_PGM           (1<<4)	/* Program Access */
> +
> +#define TSI148_LCSR_OTAT_AMODE_M       (0xf<<0)	/* Address Mode Mask */
> +#define TSI148_LCSR_OTAT_AMODE_A16     (0<<0)	/* A16 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_A24     (1<<0)	/* A24 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_A32     (2<<0)	/* A32 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_A64     (4<<0)	/* A32 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_CRCSR   (5<<0)	/* CR/CSR Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_USER1   (8<<0)	/* User1 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_USER2   (9<<0)	/* User2 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_USER3   (10<<0)	/* User3 Address Space */
> +#define TSI148_LCSR_OTAT_AMODE_USER4   (11<<0)	/* User4 Address Space */
> +
> +/*
> + *  VME Master Control Register  CRG+$234
> + */
> +#define TSI148_LCSR_VMCTRL_VSA         (1<<27)	/* VMEbus Stop Ack */
> +#define TSI148_LCSR_VMCTRL_VS          (1<<26)	/* VMEbus Stop */
> +#define TSI148_LCSR_VMCTRL_DHB         (1<<25)	/* Device Has Bus */
> +#define TSI148_LCSR_VMCTRL_DWB         (1<<24)	/* Device Wants Bus */
> +
> +#define TSI148_LCSR_VMCTRL_RMWEN       (1<<20)	/* RMW Enable */
> +
> +#define TSI148_LCSR_VMCTRL_ATO_M       (7<<16)	/* Master Access Time-out Mask */
> +#define TSI148_LCSR_VMCTRL_ATO_32      (0<<16)	/* 32 us */
> +#define TSI148_LCSR_VMCTRL_ATO_128     (1<<16)	/* 128 us */
> +#define TSI148_LCSR_VMCTRL_ATO_512     (2<<16)	/* 512 us */
> +#define TSI148_LCSR_VMCTRL_ATO_2M      (3<<16)	/* 2 ms */
> +#define TSI148_LCSR_VMCTRL_ATO_8M      (4<<16)	/* 8 ms */
> +#define TSI148_LCSR_VMCTRL_ATO_32M     (5<<16)	/* 32 ms */
> +#define TSI148_LCSR_VMCTRL_ATO_128M    (6<<16)	/* 128 ms */
> +#define TSI148_LCSR_VMCTRL_ATO_DIS     (7<<16)	/* Disabled */
> +
> +#define TSI148_LCSR_VMCTRL_VTOFF_M     (7<<12)	/* VMEbus Master Time off */
> +#define TSI148_LCSR_VMCTRL_VTOFF_0     (0<<12)	/* 0us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_1     (1<<12)	/* 1us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_2     (2<<12)	/* 2us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_4     (3<<12)	/* 4us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_8     (4<<12)	/* 8us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_16    (5<<12)	/* 16us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_32    (6<<12)	/* 32us */
> +#define TSI148_LCSR_VMCTRL_VTOFF_64    (7<<12)	/* 64us */
> +
> +#define TSI148_LCSR_VMCTRL_VTON_M      (7<<8)	/* VMEbus Master Time On */
> +#define TSI148_LCSR_VMCTRL_VTON_4      (0<<8)	/* 8us */
> +#define TSI148_LCSR_VMCTRL_VTON_8      (1<<8)	/* 8us */
> +#define TSI148_LCSR_VMCTRL_VTON_16     (2<<8)	/* 16us */
> +#define TSI148_LCSR_VMCTRL_VTON_32     (3<<8)	/* 32us */
> +#define TSI148_LCSR_VMCTRL_VTON_64     (4<<8)	/* 64us */
> +#define TSI148_LCSR_VMCTRL_VTON_128    (5<<8)	/* 128us */
> +#define TSI148_LCSR_VMCTRL_VTON_256    (6<<8)	/* 256us */
> +#define TSI148_LCSR_VMCTRL_VTON_512    (7<<8)	/* 512us */
> +
> +#define TSI148_LCSR_VMCTRL_VREL_M      (3<<3)	/* VMEbus Master Rel Mode Mask */
> +#define TSI148_LCSR_VMCTRL_VREL_T_D    (0<<3)	/* Time on or Done */
> +#define TSI148_LCSR_VMCTRL_VREL_T_R_D  (1<<3)	/* Time on and REQ or Done */
> +#define TSI148_LCSR_VMCTRL_VREL_T_B_D  (2<<3)	/* Time on and BCLR or Done */
> +#define TSI148_LCSR_VMCTRL_VREL_T_D_R  (3<<3)	/* Time on or Done and REQ */
> +
> +#define TSI148_LCSR_VMCTRL_VFAIR       (1<<2)	/* VMEbus Master Fair Mode */
> +#define TSI148_LCSR_VMCTRL_VREQL_M     (3<<0)	/* VMEbus Master Req Level Mask */
> +
> +/*
> + *  VMEbus Control Register CRG+$238
> + */
> +#define TSI148_LCSR_VCTRL_LRE          (1<<31)	/* Late Retry Enable */
> +
> +#define TSI148_LCSR_VCTRL_DLT_M        (0xF<<24)	/* Deadlock Timer */
> +#define TSI148_LCSR_VCTRL_DLT_OFF      (0<<24)	/* Deadlock Timer Off */
> +#define TSI148_LCSR_VCTRL_DLT_16       (1<<24)	/* 16 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_32       (2<<24)	/* 32 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_64       (3<<24)	/* 64 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_128      (4<<24)	/* 128 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_256      (5<<24)	/* 256 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_512      (6<<24)	/* 512 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_1024     (7<<24)	/* 1024 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_2048     (8<<24)	/* 2048 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_4096     (9<<24)	/* 4096 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_8192     (0xA<<24)	/* 8192 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_16384    (0xB<<24)	/* 16384 VCLKS */
> +#define TSI148_LCSR_VCTRL_DLT_32768    (0xC<<24)	/* 32768 VCLKS */
> +
> +#define TSI148_LCSR_VCTRL_NERBB        (1<<20)	/* No Early Release of Bus Busy */
> +
> +#define TSI148_LCSR_VCTRL_SRESET       (1<<17)	/* System Reset */
> +#define TSI148_LCSR_VCTRL_LRESET       (1<<16)	/* Local Reset */
> +
> +#define TSI148_LCSR_VCTRL_SFAILAI      (1<<15)	/* SYSFAIL Auto Slot ID */
> +#define TSI148_LCSR_VCTRL_BID_M        (0x1F<<8)	/* Broadcast ID Mask */
> +
> +#define TSI148_LCSR_VCTRL_ATOEN        (1<<7)	/* Arbiter Time-out Enable */
> +#define TSI148_LCSR_VCTRL_ROBIN        (1<<6)	/* VMEbus Round Robin */
> +
> +#define TSI148_LCSR_VCTRL_GTO_M        (7<<0)	/* VMEbus Global Time-out Mask */
> +#define TSI148_LCSR_VCTRL_GTO_8	      (0<<0)	/* 8 us */
> +#define TSI148_LCSR_VCTRL_GTO_16	      (1<<0)	/* 16 us */
> +#define TSI148_LCSR_VCTRL_GTO_32	      (2<<0)	/* 32 us */
> +#define TSI148_LCSR_VCTRL_GTO_64	      (3<<0)	/* 64 us */
> +#define TSI148_LCSR_VCTRL_GTO_128      (4<<0)	/* 128 us */
> +#define TSI148_LCSR_VCTRL_GTO_256      (5<<0)	/* 256 us */
> +#define TSI148_LCSR_VCTRL_GTO_512      (6<<0)	/* 512 us */
> +#define TSI148_LCSR_VCTRL_GTO_DIS      (7<<0)	/* Disabled */
> +
> +/*
> + *  VMEbus Status Register  CRG + $23C
> + */
> +#define TSI148_LCSR_VSTAT_CPURST       (1<<15)	/* Clear power up reset */
> +#define TSI148_LCSR_VSTAT_BRDFL        (1<<14)	/* Board fail */
> +#define TSI148_LCSR_VSTAT_PURSTS       (1<<12)	/* Power up reset status */
> +#define TSI148_LCSR_VSTAT_BDFAILS      (1<<11)	/* Board Fail Status */
> +#define TSI148_LCSR_VSTAT_SYSFAILS     (1<<10)	/* System Fail Status */
> +#define TSI148_LCSR_VSTAT_ACFAILS      (1<<9)	/* AC fail status */
> +#define TSI148_LCSR_VSTAT_SCONS        (1<<8)	/* System Cont Status */
> +#define TSI148_LCSR_VSTAT_GAP          (1<<5)	/* Geographic Addr Parity */
> +#define TSI148_LCSR_VSTAT_GA_M         (0x1F<<0)	/* Geographic Addr Mask */
> +
> +/*
> + *  PCI Configuration Status Register CRG+$240
> + */
> +#define TSI148_LCSR_PSTAT_REQ64S       (1<<6)	/* Request 64 status set */
> +#define TSI148_LCSR_PSTAT_M66ENS       (1<<5)	/* M66ENS 66Mhz enable */
> +#define TSI148_LCSR_PSTAT_FRAMES       (1<<4)	/* Frame Status */
> +#define TSI148_LCSR_PSTAT_IRDYS        (1<<3)	/* IRDY status */
> +#define TSI148_LCSR_PSTAT_DEVSELS      (1<<2)	/* DEVL status */
> +#define TSI148_LCSR_PSTAT_STOPS        (1<<1)	/* STOP status */
> +#define TSI148_LCSR_PSTAT_TRDYS        (1<<0)	/* TRDY status */
> +
> +/*
> + *  VMEbus Exception Attributes Register  CRG + $268
> + */
> +#define TSI148_LCSR_VEAT_VES           (1<<31)	/* Status */
> +#define TSI148_LCSR_VEAT_VEOF          (1<<30)	/* Overflow */
> +#define TSI148_LCSR_VEAT_VESCL         (1<<29)	/* Status Clear */
> +#define TSI148_LCSR_VEAT_2EOT          (1<<21)	/* 2e Odd Termination */
> +#define TSI148_LCSR_VEAT_2EST          (1<<20)	/* 2e Slave terminated */
> +#define TSI148_LCSR_VEAT_BERR          (1<<19)	/* Bus Error */
> +#define TSI148_LCSR_VEAT_LWORD         (1<<18)	/* LWORD_ signal state */
> +#define TSI148_LCSR_VEAT_WRITE         (1<<17)	/* WRITE_ signal state */
> +#define TSI148_LCSR_VEAT_IACK          (1<<16)	/* IACK_ signal state */
> +#define TSI148_LCSR_VEAT_DS1           (1<<15)	/* DS1_ signal state */
> +#define TSI148_LCSR_VEAT_DS0           (1<<14)	/* DS0_ signal state */
> +#define TSI148_LCSR_VEAT_AM_M          (0x3F<<8)	/* Address Mode Mask */
> +#define TSI148_LCSR_VEAT_XAM_M         (0xFF<<0)	/* Master AMode Mask */
> +
> +
> +/*
> + * VMEbus PCI Error Diagnostics PCI/X Attributes Register  CRG + $280
> + */
> +#define TSI148_LCSR_EDPAT_EDPCL        (1<<29)
> +
> +/*
> + *  Inbound Translation Starting Address Lower
> + */
> +#define TSI148_LCSR_ITSAL6432_M        (0xFFFF<<16)	/* Mask */
> +#define TSI148_LCSR_ITSAL24_M          (0x00FFF<<12)	/* Mask */
> +#define TSI148_LCSR_ITSAL16_M          (0x0000FFF<<4)	/* Mask */
> +
> +/*
> + *  Inbound Translation Ending Address Lower
> + */
> +#define TSI148_LCSR_ITEAL6432_M        (0xFFFF<<16)	/* Mask */
> +#define TSI148_LCSR_ITEAL24_M          (0x00FFF<<12)	/* Mask */
> +#define TSI148_LCSR_ITEAL16_M          (0x0000FFF<<4)	/* Mask */
> +
> +/*
> + *  Inbound Translation Offset Lower
> + */
> +#define TSI148_LCSR_ITOFFL6432_M       (0xFFFF<<16)	/* Mask */
> +#define TSI148_LCSR_ITOFFL24_M         (0xFFFFF<<12)	/* Mask */
> +#define TSI148_LCSR_ITOFFL16_M         (0xFFFFFFF<<4)	/* Mask */
> +
> +/*
> + *  Inbound Translation Attribute
> + */
> +#define TSI148_LCSR_ITAT_EN            (1<<31)	/* Window Enable */
> +#define TSI148_LCSR_ITAT_TH            (1<<18)	/* Prefetch Threshold */
> +
> +#define TSI148_LCSR_ITAT_VFS_M         (3<<16)	/* Virtual FIFO Size Mask */
> +#define TSI148_LCSR_ITAT_VFS_64        (0<<16)	/* 64 bytes Virtual FIFO Size */
> +#define TSI148_LCSR_ITAT_VFS_128       (1<<16)	/* 128 bytes Virtual FIFO Sz */
> +#define TSI148_LCSR_ITAT_VFS_256       (2<<16)	/* 256 bytes Virtual FIFO Sz */
> +#define TSI148_LCSR_ITAT_VFS_512       (3<<16)	/* 512 bytes Virtual FIFO Sz */
> +
> +#define TSI148_LCSR_ITAT_2eSSTM_M      (7<<12)	/* 2eSST Xfer Rate Mask */
> +#define TSI148_LCSR_ITAT_2eSSTM_160    (0<<12)	/* 160MB/s 2eSST Xfer Rate */
> +#define TSI148_LCSR_ITAT_2eSSTM_267    (1<<12)	/* 267MB/s 2eSST Xfer Rate */
> +#define TSI148_LCSR_ITAT_2eSSTM_320    (2<<12)	/* 320MB/s 2eSST Xfer Rate */
> +
> +#define TSI148_LCSR_ITAT_2eSSTB        (1<<11)	/* 2eSST Bcast Xfer Protocol */
> +#define TSI148_LCSR_ITAT_2eSST         (1<<10)	/* 2eSST Xfer Protocol */
> +#define TSI148_LCSR_ITAT_2eVME         (1<<9)	/* 2eVME Xfer Protocol */
> +#define TSI148_LCSR_ITAT_MBLT          (1<<8)	/* MBLT Xfer Protocol */
> +#define TSI148_LCSR_ITAT_BLT           (1<<7)	/* BLT Xfer Protocol */
> +
> +#define TSI148_LCSR_ITAT_AS_M          (7<<4)	/* Address Space Mask */
> +#define TSI148_LCSR_ITAT_AS_A16        (0<<4)	/* A16 Address Space */
> +#define TSI148_LCSR_ITAT_AS_A24        (1<<4)	/* A24 Address Space */
> +#define TSI148_LCSR_ITAT_AS_A32        (2<<4)	/* A32 Address Space */
> +#define TSI148_LCSR_ITAT_AS_A64        (4<<4)	/* A64 Address Space */
> +
> +#define TSI148_LCSR_ITAT_SUPR          (1<<3)	/* Supervisor Access */
> +#define TSI148_LCSR_ITAT_NPRIV         (1<<2)	/* Non-Priv (User) Access */
> +#define TSI148_LCSR_ITAT_PGM           (1<<1)	/* Program Access */
> +#define TSI148_LCSR_ITAT_DATA          (1<<0)	/* Data Access */
> +
> +/*
> + *  GCSR Base Address Lower Address  CRG +$404
> + */
> +#define TSI148_LCSR_GBAL_M             (0x7FFFFFF<<5)	/* Mask */
> +
> +/*
> + *  GCSR Attribute Register CRG + $408
> + */
> +#define TSI148_LCSR_GCSRAT_EN          (1<<7)	/* Enable access to GCSR */
> +
> +#define TSI148_LCSR_GCSRAT_AS_M        (7<<4)	/* Address Space Mask */
> +#define TSI148_LCSR_GCSRAT_AS_A16       (0<<4)	/* Address Space 16 */
> +#define TSI148_LCSR_GCSRAT_AS_A24       (1<<4)	/* Address Space 24 */
> +#define TSI148_LCSR_GCSRAT_AS_A32       (2<<4)	/* Address Space 32 */
> +#define TSI148_LCSR_GCSRAT_AS_A64       (4<<4)	/* Address Space 64 */
> +
> +#define TSI148_LCSR_GCSRAT_SUPR        (1<<3)	/* Sup set -GCSR decoder */
> +#define TSI148_LCSR_GCSRAT_NPRIV       (1<<2)	/* Non-Privliged set - CGSR */
> +#define TSI148_LCSR_GCSRAT_PGM         (1<<1)	/* Program set - GCSR decoder */
> +#define TSI148_LCSR_GCSRAT_DATA        (1<<0)	/* DATA set GCSR decoder */
> +
> +/*
> + *  CRG Base Address Lower Address  CRG + $410
> + */
> +#define TSI148_LCSR_CBAL_M             (0xFFFFF<<12)
> +
> +/*
> + *  CRG Attribute Register  CRG + $414
> + */
> +#define TSI148_LCSR_CRGAT_EN           (1<<7)	/* Enable PRG Access */
> +
> +#define TSI148_LCSR_CRGAT_AS_M         (7<<4)	/* Address Space */
> +#define TSI148_LCSR_CRGAT_AS_A16       (0<<4)	/* Address Space 16 */
> +#define TSI148_LCSR_CRGAT_AS_A24       (1<<4)	/* Address Space 24 */
> +#define TSI148_LCSR_CRGAT_AS_A32       (2<<4)	/* Address Space 32 */
> +#define TSI148_LCSR_CRGAT_AS_A64       (4<<4)	/* Address Space 64 */
> +
> +#define TSI148_LCSR_CRGAT_SUPR         (1<<3)	/* Supervisor Access */
> +#define TSI148_LCSR_CRGAT_NPRIV        (1<<2)	/* Non-Privliged(User) Access */
> +#define TSI148_LCSR_CRGAT_PGM          (1<<1)	/* Program Access */
> +#define TSI148_LCSR_CRGAT_DATA         (1<<0)	/* Data Access */
> +
> +/*
> + *  CR/CSR Offset Lower Register  CRG + $41C
> + */
> +#define TSI148_LCSR_CROL_M             (0x1FFF<<19)	/* Mask */
> +
> +/*
> + *  CR/CSR Attribute register  CRG + $420
> + */
> +#define TSI148_LCSR_CRAT_EN            (1<<7)	/* Enable access to CR/CSR */
> +
> +/*
> + *  Location Monitor base address lower register  CRG + $428
> + */
> +#define TSI148_LCSR_LMBAL_M            (0x7FFFFFF<<5)	/* Mask */
> +
> +/*
> + *  Location Monitor Attribute Register  CRG + $42C
> + */
> +#define TSI148_LCSR_LMAT_EN            (1<<7)	/* Enable Location Monitor */
> +
> +#define TSI148_LCSR_LMAT_AS_M          (7<<4)	/* Address Space MASK  */
> +#define TSI148_LCSR_LMAT_AS_A16        (0<<4)	/* A16 */
> +#define TSI148_LCSR_LMAT_AS_A24        (1<<4)	/* A24 */
> +#define TSI148_LCSR_LMAT_AS_A32        (2<<4)	/* A32 */
> +#define TSI148_LCSR_LMAT_AS_A64        (4<<4)	/* A64 */
> +
> +#define TSI148_LCSR_LMAT_SUPR          (1<<3)	/* Supervisor Access */
> +#define TSI148_LCSR_LMAT_NPRIV         (1<<2)	/* Non-Priv (User) Access */
> +#define TSI148_LCSR_LMAT_PGM           (1<<1)	/* Program Access */
> +#define TSI148_LCSR_LMAT_DATA          (1<<0)	/* Data Access  */
> +
> +/*
> + *  Broadcast Pulse Generator Timer Register  CRG + $438
> + */
> +#define TSI148_LCSR_BPGTR_BPGT_M       (0xFFFF<<0)	/* Mask */
> +
> +/*
> + *  Broadcast Programmable Clock Timer Register  CRG + $43C
> + */
> +#define TSI148_LCSR_BPCTR_BPCT_M       (0xFFFFFF<<0)	/* Mask */
> +
> +/*
> + *  VMEbus Interrupt Control Register           CRG + $43C
> + */
> +#define TSI148_LCSR_VICR_CNTS_M        (3<<22)	/* Cntr Source MASK */
> +#define TSI148_LCSR_VICR_CNTS_DIS      (1<<22)	/* Cntr Disable */
> +#define TSI148_LCSR_VICR_CNTS_IRQ1     (2<<22)	/* IRQ1 to Cntr */
> +#define TSI148_LCSR_VICR_CNTS_IRQ2     (3<<22)	/* IRQ2 to Cntr */
> +
> +#define TSI148_LCSR_VICR_EDGIS_M       (3<<20)	/* Edge interupt MASK */
> +#define TSI148_LCSR_VICR_EDGIS_DIS     (1<<20)	/* Edge interupt Disable */
> +#define TSI148_LCSR_VICR_EDGIS_IRQ1    (2<<20)	/* IRQ1 to Edge */
> +#define TSI148_LCSR_VICR_EDGIS_IRQ2    (3<<20)	/* IRQ2 to Edge */
> +
> +#define TSI148_LCSR_VICR_IRQIF_M       (3<<18)	/* IRQ1* Function MASK */
> +#define TSI148_LCSR_VICR_IRQIF_NORM    (1<<18)	/* Normal */
> +#define TSI148_LCSR_VICR_IRQIF_PULSE   (2<<18)	/* Pulse Generator */
> +#define TSI148_LCSR_VICR_IRQIF_PROG    (3<<18)	/* Programmable Clock */
> +#define TSI148_LCSR_VICR_IRQIF_1U      (4<<18)	/* 1us Clock */
> +
> +#define TSI148_LCSR_VICR_IRQ2F_M       (3<<16)	/* IRQ2* Function MASK */
> +#define TSI148_LCSR_VICR_IRQ2F_NORM    (1<<16)	/* Normal */
> +#define TSI148_LCSR_VICR_IRQ2F_PULSE   (2<<16)	/* Pulse Generator */
> +#define TSI148_LCSR_VICR_IRQ2F_PROG    (3<<16)	/* Programmable Clock */
> +#define TSI148_LCSR_VICR_IRQ2F_1U      (4<<16)	/* 1us Clock */
> +
> +#define TSI148_LCSR_VICR_BIP           (1<<15)	/* Broadcast Interrupt Pulse */
> +
> +#define TSI148_LCSR_VICR_IRQC          (1<<12)	/* VMEbus IRQ Clear */
> +#define TSI148_LCSR_VICR_IRQS          (1<<11)	/* VMEbus IRQ Status */
> +
> +#define TSI148_LCSR_VICR_IRQL_M        (7<<8)	/* VMEbus SW IRQ Level Mask */
> +#define TSI148_LCSR_VICR_IRQL_1        (1<<8)	/* VMEbus SW IRQ Level 1 */
> +#define TSI148_LCSR_VICR_IRQL_2        (2<<8)	/* VMEbus SW IRQ Level 2 */
> +#define TSI148_LCSR_VICR_IRQL_3        (3<<8)	/* VMEbus SW IRQ Level 3 */
> +#define TSI148_LCSR_VICR_IRQL_4        (4<<8)	/* VMEbus SW IRQ Level 4 */
> +#define TSI148_LCSR_VICR_IRQL_5        (5<<8)	/* VMEbus SW IRQ Level 5 */
> +#define TSI148_LCSR_VICR_IRQL_6        (6<<8)	/* VMEbus SW IRQ Level 6 */
> +#define TSI148_LCSR_VICR_IRQL_7        (7<<8)	/* VMEbus SW IRQ Level 7 */
> +
> +static const int TSI148_LCSR_VICR_IRQL[8] = { 0, TSI148_LCSR_VICR_IRQL_1,
> +			TSI148_LCSR_VICR_IRQL_2, TSI148_LCSR_VICR_IRQL_3,
> +			TSI148_LCSR_VICR_IRQL_4, TSI148_LCSR_VICR_IRQL_5,
> +			TSI148_LCSR_VICR_IRQL_6, TSI148_LCSR_VICR_IRQL_7 };
> +
> +#define TSI148_LCSR_VICR_STID_M        (0xFF<<0)	/* Status/ID Mask */
> +
> +/*
> + *  Interrupt Enable Register   CRG + $440
> + */
> +#define TSI148_LCSR_INTEN_DMA1EN       (1<<25)	/* DMAC 1 */
> +#define TSI148_LCSR_INTEN_DMA0EN       (1<<24)	/* DMAC 0 */
> +#define TSI148_LCSR_INTEN_LM3EN        (1<<23)	/* Location Monitor 3 */
> +#define TSI148_LCSR_INTEN_LM2EN        (1<<22)	/* Location Monitor 2 */
> +#define TSI148_LCSR_INTEN_LM1EN        (1<<21)	/* Location Monitor 1 */
> +#define TSI148_LCSR_INTEN_LM0EN        (1<<20)	/* Location Monitor 0 */
> +#define TSI148_LCSR_INTEN_MB3EN        (1<<19)	/* Mail Box 3 */
> +#define TSI148_LCSR_INTEN_MB2EN        (1<<18)	/* Mail Box 2 */
> +#define TSI148_LCSR_INTEN_MB1EN        (1<<17)	/* Mail Box 1 */
> +#define TSI148_LCSR_INTEN_MB0EN        (1<<16)	/* Mail Box 0 */
> +#define TSI148_LCSR_INTEN_PERREN       (1<<13)	/* PCI/X Error */
> +#define TSI148_LCSR_INTEN_VERREN       (1<<12)	/* VMEbus Error */
> +#define TSI148_LCSR_INTEN_VIEEN        (1<<11)	/* VMEbus IRQ Edge */
> +#define TSI148_LCSR_INTEN_IACKEN       (1<<10)	/* IACK */
> +#define TSI148_LCSR_INTEN_SYSFLEN      (1<<9)	/* System Fail */
> +#define TSI148_LCSR_INTEN_ACFLEN       (1<<8)	/* AC Fail */
> +#define TSI148_LCSR_INTEN_IRQ7EN       (1<<7)	/* IRQ7 */
> +#define TSI148_LCSR_INTEN_IRQ6EN       (1<<6)	/* IRQ6 */
> +#define TSI148_LCSR_INTEN_IRQ5EN       (1<<5)	/* IRQ5 */
> +#define TSI148_LCSR_INTEN_IRQ4EN       (1<<4)	/* IRQ4 */
> +#define TSI148_LCSR_INTEN_IRQ3EN       (1<<3)	/* IRQ3 */
> +#define TSI148_LCSR_INTEN_IRQ2EN       (1<<2)	/* IRQ2 */
> +#define TSI148_LCSR_INTEN_IRQ1EN       (1<<1)	/* IRQ1 */
> +
> +static const int TSI148_LCSR_INTEN_LMEN[4] = { TSI148_LCSR_INTEN_LM0EN,
> +					TSI148_LCSR_INTEN_LM1EN,
> +					TSI148_LCSR_INTEN_LM2EN,
> +					TSI148_LCSR_INTEN_LM3EN };
> +
> +static const int TSI148_LCSR_INTEN_IRQEN[7] = { TSI148_LCSR_INTEN_IRQ1EN,
> +					TSI148_LCSR_INTEN_IRQ2EN,
> +					TSI148_LCSR_INTEN_IRQ3EN,
> +					TSI148_LCSR_INTEN_IRQ4EN,
> +					TSI148_LCSR_INTEN_IRQ5EN,
> +					TSI148_LCSR_INTEN_IRQ6EN,
> +					TSI148_LCSR_INTEN_IRQ7EN };
> +
> +/*
> + *  Interrupt Enable Out Register CRG + $444
> + */
> +#define TSI148_LCSR_INTEO_DMA1EO       (1<<25)	/* DMAC 1 */
> +#define TSI148_LCSR_INTEO_DMA0EO       (1<<24)	/* DMAC 0 */
> +#define TSI148_LCSR_INTEO_LM3EO        (1<<23)	/* Loc Monitor 3 */
> +#define TSI148_LCSR_INTEO_LM2EO        (1<<22)	/* Loc Monitor 2 */
> +#define TSI148_LCSR_INTEO_LM1EO        (1<<21)	/* Loc Monitor 1 */
> +#define TSI148_LCSR_INTEO_LM0EO        (1<<20)	/* Location Monitor 0 */
> +#define TSI148_LCSR_INTEO_MB3EO        (1<<19)	/* Mail Box 3 */
> +#define TSI148_LCSR_INTEO_MB2EO        (1<<18)	/* Mail Box 2 */
> +#define TSI148_LCSR_INTEO_MB1EO        (1<<17)	/* Mail Box 1 */
> +#define TSI148_LCSR_INTEO_MB0EO        (1<<16)	/* Mail Box 0 */
> +#define TSI148_LCSR_INTEO_PERREO       (1<<13)	/* PCI/X Error */
> +#define TSI148_LCSR_INTEO_VERREO       (1<<12)	/* VMEbus Error */
> +#define TSI148_LCSR_INTEO_VIEEO        (1<<11)	/* VMEbus IRQ Edge */
> +#define TSI148_LCSR_INTEO_IACKEO       (1<<10)	/* IACK */
> +#define TSI148_LCSR_INTEO_SYSFLEO      (1<<9)	/* System Fail */
> +#define TSI148_LCSR_INTEO_ACFLEO       (1<<8)	/* AC Fail */
> +#define TSI148_LCSR_INTEO_IRQ7EO       (1<<7)	/* IRQ7 */
> +#define TSI148_LCSR_INTEO_IRQ6EO       (1<<6)	/* IRQ6 */
> +#define TSI148_LCSR_INTEO_IRQ5EO       (1<<5)	/* IRQ5 */
> +#define TSI148_LCSR_INTEO_IRQ4EO       (1<<4)	/* IRQ4 */
> +#define TSI148_LCSR_INTEO_IRQ3EO       (1<<3)	/* IRQ3 */
> +#define TSI148_LCSR_INTEO_IRQ2EO       (1<<2)	/* IRQ2 */
> +#define TSI148_LCSR_INTEO_IRQ1EO       (1<<1)	/* IRQ1 */
> +
> +static const int TSI148_LCSR_INTEO_LMEO[4] = { TSI148_LCSR_INTEO_LM0EO,
> +					TSI148_LCSR_INTEO_LM1EO,
> +					TSI148_LCSR_INTEO_LM2EO,
> +					TSI148_LCSR_INTEO_LM3EO };
> +
> +static const int TSI148_LCSR_INTEO_IRQEO[7] = { TSI148_LCSR_INTEO_IRQ1EO,
> +					TSI148_LCSR_INTEO_IRQ2EO,
> +					TSI148_LCSR_INTEO_IRQ3EO,
> +					TSI148_LCSR_INTEO_IRQ4EO,
> +					TSI148_LCSR_INTEO_IRQ5EO,
> +					TSI148_LCSR_INTEO_IRQ6EO,
> +					TSI148_LCSR_INTEO_IRQ7EO };
> +
> +/*
> + *  Interrupt Status Register CRG + $448
> + */
> +#define TSI148_LCSR_INTS_DMA1S         (1<<25)	/* DMA 1 */
> +#define TSI148_LCSR_INTS_DMA0S         (1<<24)	/* DMA 0 */
> +#define TSI148_LCSR_INTS_LM3S          (1<<23)	/* Location Monitor 3 */
> +#define TSI148_LCSR_INTS_LM2S          (1<<22)	/* Location Monitor 2 */
> +#define TSI148_LCSR_INTS_LM1S          (1<<21)	/* Location Monitor 1 */
> +#define TSI148_LCSR_INTS_LM0S          (1<<20)	/* Location Monitor 0 */
> +#define TSI148_LCSR_INTS_MB3S          (1<<19)	/* Mail Box 3 */
> +#define TSI148_LCSR_INTS_MB2S          (1<<18)	/* Mail Box 2 */
> +#define TSI148_LCSR_INTS_MB1S          (1<<17)	/* Mail Box 1 */
> +#define TSI148_LCSR_INTS_MB0S          (1<<16)	/* Mail Box 0 */
> +#define TSI148_LCSR_INTS_PERRS         (1<<13)	/* PCI/X Error */
> +#define TSI148_LCSR_INTS_VERRS         (1<<12)	/* VMEbus Error */
> +#define TSI148_LCSR_INTS_VIES          (1<<11)	/* VMEbus IRQ Edge */
> +#define TSI148_LCSR_INTS_IACKS         (1<<10)	/* IACK */
> +#define TSI148_LCSR_INTS_SYSFLS        (1<<9)	/* System Fail */
> +#define TSI148_LCSR_INTS_ACFLS         (1<<8)	/* AC Fail */
> +#define TSI148_LCSR_INTS_IRQ7S         (1<<7)	/* IRQ7 */
> +#define TSI148_LCSR_INTS_IRQ6S         (1<<6)	/* IRQ6 */
> +#define TSI148_LCSR_INTS_IRQ5S         (1<<5)	/* IRQ5 */
> +#define TSI148_LCSR_INTS_IRQ4S         (1<<4)	/* IRQ4 */
> +#define TSI148_LCSR_INTS_IRQ3S         (1<<3)	/* IRQ3 */
> +#define TSI148_LCSR_INTS_IRQ2S         (1<<2)	/* IRQ2 */
> +#define TSI148_LCSR_INTS_IRQ1S         (1<<1)	/* IRQ1 */
> +
> +static const int TSI148_LCSR_INTS_LMS[4] = { TSI148_LCSR_INTS_LM0S,
> +					TSI148_LCSR_INTS_LM1S,
> +					TSI148_LCSR_INTS_LM2S,
> +					TSI148_LCSR_INTS_LM3S };
> +
> +static const int TSI148_LCSR_INTS_MBS[4] = { TSI148_LCSR_INTS_MB0S,
> +					TSI148_LCSR_INTS_MB1S,
> +					TSI148_LCSR_INTS_MB2S,
> +					TSI148_LCSR_INTS_MB3S };
> +
> +/*
> + *  Interrupt Clear Register CRG + $44C
> + */
> +#define TSI148_LCSR_INTC_DMA1C         (1<<25)	/* DMA 1 */
> +#define TSI148_LCSR_INTC_DMA0C         (1<<24)	/* DMA 0 */
> +#define TSI148_LCSR_INTC_LM3C          (1<<23)	/* Location Monitor 3 */
> +#define TSI148_LCSR_INTC_LM2C          (1<<22)	/* Location Monitor 2 */
> +#define TSI148_LCSR_INTC_LM1C          (1<<21)	/* Location Monitor 1 */
> +#define TSI148_LCSR_INTC_LM0C          (1<<20)	/* Location Monitor 0 */
> +#define TSI148_LCSR_INTC_MB3C          (1<<19)	/* Mail Box 3 */
> +#define TSI148_LCSR_INTC_MB2C          (1<<18)	/* Mail Box 2 */
> +#define TSI148_LCSR_INTC_MB1C          (1<<17)	/* Mail Box 1 */
> +#define TSI148_LCSR_INTC_MB0C          (1<<16)	/* Mail Box 0 */
> +#define TSI148_LCSR_INTC_PERRC         (1<<13)	/* VMEbus Error */
> +#define TSI148_LCSR_INTC_VERRC         (1<<12)	/* VMEbus Access Time-out */
> +#define TSI148_LCSR_INTC_VIEC          (1<<11)	/* VMEbus IRQ Edge */
> +#define TSI148_LCSR_INTC_IACKC         (1<<10)	/* IACK */
> +#define TSI148_LCSR_INTC_SYSFLC        (1<<9)	/* System Fail */
> +#define TSI148_LCSR_INTC_ACFLC         (1<<8)	/* AC Fail */
> +
> +static const int TSI148_LCSR_INTC_LMC[4] = { TSI148_LCSR_INTC_LM0C,
> +					TSI148_LCSR_INTC_LM1C,
> +					TSI148_LCSR_INTC_LM2C,
> +					TSI148_LCSR_INTC_LM3C };
> +
> +static const int TSI148_LCSR_INTC_MBC[4] = { TSI148_LCSR_INTC_MB0C,
> +					TSI148_LCSR_INTC_MB1C,
> +					TSI148_LCSR_INTC_MB2C,
> +					TSI148_LCSR_INTC_MB3C };
> +
> +/*
> + *  Interrupt Map Register 1 CRG + $458
> + */
> +#define TSI148_LCSR_INTM1_DMA1M_M      (3<<18)	/* DMA 1 */
> +#define TSI148_LCSR_INTM1_DMA0M_M      (3<<16)	/* DMA 0 */
> +#define TSI148_LCSR_INTM1_LM3M_M       (3<<14)	/* Location Monitor 3 */
> +#define TSI148_LCSR_INTM1_LM2M_M       (3<<12)	/* Location Monitor 2 */
> +#define TSI148_LCSR_INTM1_LM1M_M       (3<<10)	/* Location Monitor 1 */
> +#define TSI148_LCSR_INTM1_LM0M_M       (3<<8)	/* Location Monitor 0 */
> +#define TSI148_LCSR_INTM1_MB3M_M       (3<<6)	/* Mail Box 3 */
> +#define TSI148_LCSR_INTM1_MB2M_M       (3<<4)	/* Mail Box 2 */
> +#define TSI148_LCSR_INTM1_MB1M_M       (3<<2)	/* Mail Box 1 */
> +#define TSI148_LCSR_INTM1_MB0M_M       (3<<0)	/* Mail Box 0 */
> +
> +/*
> + *  Interrupt Map Register 2 CRG + $45C
> + */
> +#define TSI148_LCSR_INTM2_PERRM_M      (3<<26)	/* PCI Bus Error */
> +#define TSI148_LCSR_INTM2_VERRM_M      (3<<24)	/* VMEbus Error */
> +#define TSI148_LCSR_INTM2_VIEM_M       (3<<22)	/* VMEbus IRQ Edge */
> +#define TSI148_LCSR_INTM2_IACKM_M      (3<<20)	/* IACK */
> +#define TSI148_LCSR_INTM2_SYSFLM_M     (3<<18)	/* System Fail */
> +#define TSI148_LCSR_INTM2_ACFLM_M      (3<<16)	/* AC Fail */
> +#define TSI148_LCSR_INTM2_IRQ7M_M      (3<<14)	/* IRQ7 */
> +#define TSI148_LCSR_INTM2_IRQ6M_M      (3<<12)	/* IRQ6 */
> +#define TSI148_LCSR_INTM2_IRQ5M_M      (3<<10)	/* IRQ5 */
> +#define TSI148_LCSR_INTM2_IRQ4M_M      (3<<8)	/* IRQ4 */
> +#define TSI148_LCSR_INTM2_IRQ3M_M      (3<<6)	/* IRQ3 */
> +#define TSI148_LCSR_INTM2_IRQ2M_M      (3<<4)	/* IRQ2 */
> +#define TSI148_LCSR_INTM2_IRQ1M_M      (3<<2)	/* IRQ1 */
> +
> +/*
> + *  DMA Control (0-1) Registers CRG + $500
> + */
> +#define TSI148_LCSR_DCTL_ABT           (1<<27)	/* Abort */
> +#define TSI148_LCSR_DCTL_PAU           (1<<26)	/* Pause */
> +#define TSI148_LCSR_DCTL_DGO           (1<<25)	/* DMA Go */
> +
> +#define TSI148_LCSR_DCTL_MOD           (1<<23)	/* Mode */
> +
> +#define TSI148_LCSR_DCTL_VBKS_M        (7<<12)	/* VMEbus block Size MASK */
> +#define TSI148_LCSR_DCTL_VBKS_32       (0<<12)	/* VMEbus block Size 32 */
> +#define TSI148_LCSR_DCTL_VBKS_64       (1<<12)	/* VMEbus block Size 64 */
> +#define TSI148_LCSR_DCTL_VBKS_128      (2<<12)	/* VMEbus block Size 128 */
> +#define TSI148_LCSR_DCTL_VBKS_256      (3<<12)	/* VMEbus block Size 256 */
> +#define TSI148_LCSR_DCTL_VBKS_512      (4<<12)	/* VMEbus block Size 512 */
> +#define TSI148_LCSR_DCTL_VBKS_1024     (5<<12)	/* VMEbus block Size 1024 */
> +#define TSI148_LCSR_DCTL_VBKS_2048     (6<<12)	/* VMEbus block Size 2048 */
> +#define TSI148_LCSR_DCTL_VBKS_4096     (7<<12)	/* VMEbus block Size 4096 */
> +
> +#define TSI148_LCSR_DCTL_VBOT_M        (7<<8)	/* VMEbus back-off MASK */
> +#define TSI148_LCSR_DCTL_VBOT_0        (0<<8)	/* VMEbus back-off  0us */
> +#define TSI148_LCSR_DCTL_VBOT_1        (1<<8)	/* VMEbus back-off 1us */
> +#define TSI148_LCSR_DCTL_VBOT_2        (2<<8)	/* VMEbus back-off 2us */
> +#define TSI148_LCSR_DCTL_VBOT_4        (3<<8)	/* VMEbus back-off 4us */
> +#define TSI148_LCSR_DCTL_VBOT_8        (4<<8)	/* VMEbus back-off 8us */
> +#define TSI148_LCSR_DCTL_VBOT_16       (5<<8)	/* VMEbus back-off 16us */
> +#define TSI148_LCSR_DCTL_VBOT_32       (6<<8)	/* VMEbus back-off 32us */
> +#define TSI148_LCSR_DCTL_VBOT_64       (7<<8)	/* VMEbus back-off 64us */
> +
> +#define TSI148_LCSR_DCTL_PBKS_M        (7<<4)	/* PCI block size MASK */
> +#define TSI148_LCSR_DCTL_PBKS_32       (0<<4)	/* PCI block size 32 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_64       (1<<4)	/* PCI block size 64 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_128      (2<<4)	/* PCI block size 128 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_256      (3<<4)	/* PCI block size 256 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_512      (4<<4)	/* PCI block size 512 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_1024     (5<<4)	/* PCI block size 1024 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_2048     (6<<4)	/* PCI block size 2048 bytes */
> +#define TSI148_LCSR_DCTL_PBKS_4096     (7<<4)	/* PCI block size 4096 bytes */
> +
> +#define TSI148_LCSR_DCTL_PBOT_M        (7<<0)	/* PCI back off MASK */
> +#define TSI148_LCSR_DCTL_PBOT_0        (0<<0)	/* PCI back off 0us */
> +#define TSI148_LCSR_DCTL_PBOT_1        (1<<0)	/* PCI back off 1us */
> +#define TSI148_LCSR_DCTL_PBOT_2        (2<<0)	/* PCI back off 2us */
> +#define TSI148_LCSR_DCTL_PBOT_4        (3<<0)	/* PCI back off 3us */
> +#define TSI148_LCSR_DCTL_PBOT_8        (4<<0)	/* PCI back off 4us */
> +#define TSI148_LCSR_DCTL_PBOT_16       (5<<0)	/* PCI back off 8us */
> +#define TSI148_LCSR_DCTL_PBOT_32       (6<<0)	/* PCI back off 16us */
> +#define TSI148_LCSR_DCTL_PBOT_64       (7<<0)	/* PCI back off 32us */
> +
> +/*
> + *  DMA Status Registers (0-1)  CRG + $504
> + */
> +#define TSI148_LCSR_DSTA_SMA           (1<<31)	/* PCI Signalled Master Abt */
> +#define TSI148_LCSR_DSTA_RTA           (1<<30)	/* PCI Received Target Abt */
> +#define TSI148_LCSR_DSTA_MRC           (1<<29)	/* PCI Max Retry Count */
> +#define TSI148_LCSR_DSTA_VBE           (1<<28)	/* VMEbus error */
> +#define TSI148_LCSR_DSTA_ABT           (1<<27)	/* Abort */
> +#define TSI148_LCSR_DSTA_PAU           (1<<26)	/* Pause */
> +#define TSI148_LCSR_DSTA_DON           (1<<25)	/* Done */
> +#define TSI148_LCSR_DSTA_BSY           (1<<24)	/* Busy */
> +
> +/*
> + *  DMA Current Link Address Lower (0-1)
> + */
> +#define TSI148_LCSR_DCLAL_M            (0x3FFFFFF<<6)	/* Mask */
> +
> +/*
> + *  DMA Source Attribute (0-1) Reg
> + */
> +#define TSI148_LCSR_DSAT_TYP_M         (3<<28)	/* Source Bus Type */
> +#define TSI148_LCSR_DSAT_TYP_PCI       (0<<28)	/* PCI Bus */
> +#define TSI148_LCSR_DSAT_TYP_VME       (1<<28)	/* VMEbus */
> +#define TSI148_LCSR_DSAT_TYP_PAT       (2<<28)	/* Data Pattern */
> +
> +#define TSI148_LCSR_DSAT_PSZ           (1<<25)	/* Pattern Size */
> +#define TSI148_LCSR_DSAT_NIN           (1<<24)	/* No Increment */
> +
> +#define TSI148_LCSR_DSAT_2eSSTM_M      (3<<11)	/* 2eSST Trans Rate Mask */
> +#define TSI148_LCSR_DSAT_2eSSTM_160    (0<<11)	/* 160 MB/s */
> +#define TSI148_LCSR_DSAT_2eSSTM_267    (1<<11)	/* 267 MB/s */
> +#define TSI148_LCSR_DSAT_2eSSTM_320    (2<<11)	/* 320 MB/s */
> +
> +#define TSI148_LCSR_DSAT_TM_M          (7<<8)	/* Bus Transfer Protocol Mask */
> +#define TSI148_LCSR_DSAT_TM_SCT        (0<<8)	/* SCT */
> +#define TSI148_LCSR_DSAT_TM_BLT        (1<<8)	/* BLT */
> +#define TSI148_LCSR_DSAT_TM_MBLT       (2<<8)	/* MBLT */
> +#define TSI148_LCSR_DSAT_TM_2eVME      (3<<8)	/* 2eVME */
> +#define TSI148_LCSR_DSAT_TM_2eSST      (4<<8)	/* 2eSST */
> +#define TSI148_LCSR_DSAT_TM_2eSSTB     (5<<8)	/* 2eSST Broadcast */
> +
> +#define TSI148_LCSR_DSAT_DBW_M         (3<<6)	/* Max Data Width MASK */
> +#define TSI148_LCSR_DSAT_DBW_16        (0<<6)	/* 16 Bits */
> +#define TSI148_LCSR_DSAT_DBW_32        (1<<6)	/* 32 Bits */
> +
> +#define TSI148_LCSR_DSAT_SUP           (1<<5)	/* Supervisory Mode */
> +#define TSI148_LCSR_DSAT_PGM           (1<<4)	/* Program Mode */
> +
> +#define TSI148_LCSR_DSAT_AMODE_M       (0xf<<0)	/* Address Space Mask */
> +#define TSI148_LCSR_DSAT_AMODE_A16     (0<<0)	/* A16 */
> +#define TSI148_LCSR_DSAT_AMODE_A24     (1<<0)	/* A24 */
> +#define TSI148_LCSR_DSAT_AMODE_A32     (2<<0)	/* A32 */
> +#define TSI148_LCSR_DSAT_AMODE_A64     (4<<0)	/* A64 */
> +#define TSI148_LCSR_DSAT_AMODE_CRCSR   (5<<0)	/* CR/CSR */
> +#define TSI148_LCSR_DSAT_AMODE_USER1   (8<<0)	/* User1 */
> +#define TSI148_LCSR_DSAT_AMODE_USER2   (9<<0)	/* User2 */
> +#define TSI148_LCSR_DSAT_AMODE_USER3   (0xa<<0)	/* User3 */
> +#define TSI148_LCSR_DSAT_AMODE_USER4   (0xb<<0)	/* User4 */
> +
> +/*
> + *  DMA Destination Attribute Registers (0-1)
> + */
> +#define TSI148_LCSR_DDAT_TYP_PCI       (0<<28)	/* Destination PCI Bus  */
> +#define TSI148_LCSR_DDAT_TYP_VME       (1<<28)	/* Destination VMEbus */
> +
> +#define TSI148_LCSR_DDAT_2eSSTM_M      (3<<11)	/* 2eSST Transfer Rate Mask */
> +#define TSI148_LCSR_DDAT_2eSSTM_160    (0<<11)	/* 160 MB/s */
> +#define TSI148_LCSR_DDAT_2eSSTM_267    (1<<11)	/* 267 MB/s */
> +#define TSI148_LCSR_DDAT_2eSSTM_320    (2<<11)	/* 320 MB/s */
> +
> +#define TSI148_LCSR_DDAT_TM_M          (7<<8)	/* Bus Transfer Protocol Mask */
> +#define TSI148_LCSR_DDAT_TM_SCT        (0<<8)	/* SCT */
> +#define TSI148_LCSR_DDAT_TM_BLT        (1<<8)	/* BLT */
> +#define TSI148_LCSR_DDAT_TM_MBLT       (2<<8)	/* MBLT */
> +#define TSI148_LCSR_DDAT_TM_2eVME      (3<<8)	/* 2eVME */
> +#define TSI148_LCSR_DDAT_TM_2eSST      (4<<8)	/* 2eSST */
> +#define TSI148_LCSR_DDAT_TM_2eSSTB     (5<<8)	/* 2eSST Broadcast */
> +
> +#define TSI148_LCSR_DDAT_DBW_M         (3<<6)	/* Max Data Width MASK */
> +#define TSI148_LCSR_DDAT_DBW_16        (0<<6)	/* 16 Bits */
> +#define TSI148_LCSR_DDAT_DBW_32        (1<<6)	/* 32 Bits */
> +
> +#define TSI148_LCSR_DDAT_SUP           (1<<5)	/* Supervisory/User Access */
> +#define TSI148_LCSR_DDAT_PGM           (1<<4)	/* Program/Data Access */
> +
> +#define TSI148_LCSR_DDAT_AMODE_M       (0xf<<0)	/* Address Space Mask */
> +#define TSI148_LCSR_DDAT_AMODE_A16      (0<<0)	/* A16 */
> +#define TSI148_LCSR_DDAT_AMODE_A24      (1<<0)	/* A24 */
> +#define TSI148_LCSR_DDAT_AMODE_A32      (2<<0)	/* A32 */
> +#define TSI148_LCSR_DDAT_AMODE_A64      (4<<0)	/* A64 */
> +#define TSI148_LCSR_DDAT_AMODE_CRCSR   (5<<0)	/* CRC/SR */
> +#define TSI148_LCSR_DDAT_AMODE_USER1   (8<<0)	/* User1 */
> +#define TSI148_LCSR_DDAT_AMODE_USER2   (9<<0)	/* User2 */
> +#define TSI148_LCSR_DDAT_AMODE_USER3   (0xa<<0)	/* User3 */
> +#define TSI148_LCSR_DDAT_AMODE_USER4   (0xb<<0)	/* User4 */
> +
> +/*
> + *  DMA Next Link Address Lower
> + */
> +#define TSI148_LCSR_DNLAL_DNLAL_M      (0x3FFFFFF<<6)	/* Address Mask */
> +#define TSI148_LCSR_DNLAL_LLA          (1<<0)	/* Last Link Address Indicator */
> +
> +/*
> + *  DMA 2eSST Broadcast Select
> + */
> +#define TSI148_LCSR_DBS_M              (0x1FFFFF<<0)	/* Mask */
> +
> +/*
> + *  GCSR Register Group
> + */
> +
> +/*
> + *  GCSR Control and Status Register  CRG + $604
> + */
> +#define TSI148_GCSR_GCTRL_LRST         (1<<15)	/* Local Reset */
> +#define TSI148_GCSR_GCTRL_SFAILEN      (1<<14)	/* System Fail enable */
> +#define TSI148_GCSR_GCTRL_BDFAILS      (1<<13)	/* Board Fail Status */
> +#define TSI148_GCSR_GCTRL_SCON         (1<<12)	/* System Copntroller */
> +#define TSI148_GCSR_GCTRL_MEN          (1<<11)	/* Module Enable (READY) */
> +
> +#define TSI148_GCSR_GCTRL_LMI3S        (1<<7)	/* Loc Monitor 3 Int Status */
> +#define TSI148_GCSR_GCTRL_LMI2S        (1<<6)	/* Loc Monitor 2 Int Status */
> +#define TSI148_GCSR_GCTRL_LMI1S        (1<<5)	/* Loc Monitor 1 Int Status */
> +#define TSI148_GCSR_GCTRL_LMI0S        (1<<4)	/* Loc Monitor 0 Int Status */
> +#define TSI148_GCSR_GCTRL_MBI3S        (1<<3)	/* Mail box 3 Int Status */
> +#define TSI148_GCSR_GCTRL_MBI2S        (1<<2)	/* Mail box 2 Int Status */
> +#define TSI148_GCSR_GCTRL_MBI1S        (1<<1)	/* Mail box 1 Int Status */
> +#define TSI148_GCSR_GCTRL_MBI0S        (1<<0)	/* Mail box 0 Int Status */
> +
> +#define TSI148_GCSR_GAP                (1<<5)	/* Geographic Addr Parity */
> +#define TSI148_GCSR_GA_M               (0x1F<<0)	/* Geographic Address Mask */
> +
> +/*
> + *  CR/CSR Register Group
> + */
> +
> +/*
> + *  CR/CSR Bit Clear Register CRG + $FF4
> + */
> +#define TSI148_CRCSR_CSRBCR_LRSTC      (1<<7)	/* Local Reset Clear */
> +#define TSI148_CRCSR_CSRBCR_SFAILC     (1<<6)	/* System Fail Enable Clear */
> +#define TSI148_CRCSR_CSRBCR_BDFAILS    (1<<5)	/* Board Fail Status */
> +#define TSI148_CRCSR_CSRBCR_MENC       (1<<4)	/* Module Enable Clear */
> +#define TSI148_CRCSR_CSRBCR_BERRSC     (1<<3)	/* Bus Error Status Clear */
> +
> +/*
> + *  CR/CSR Bit Set Register CRG+$FF8
> + */
> +#define TSI148_CRCSR_CSRBSR_LISTS      (1<<7)	/* Local Reset Clear */
> +#define TSI148_CRCSR_CSRBSR_SFAILS     (1<<6)	/* System Fail Enable Clear */
> +#define TSI148_CRCSR_CSRBSR_BDFAILS    (1<<5)	/* Board Fail Status */
> +#define TSI148_CRCSR_CSRBSR_MENS       (1<<4)	/* Module Enable Clear */
> +#define TSI148_CRCSR_CSRBSR_BERRS      (1<<3)	/* Bus Error Status Clear */
> +
> +/*
> + *  CR/CSR Base Address Register CRG + FFC
> + */
> +#define TSI148_CRCSR_CBAR_M            (0x1F<<3)	/* Mask */
> +
> +#endif				/* TSI148_H */
> 
> 

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-08 23:22     ` Emilio G. Cota
@ 2009-08-09 12:17       ` Emilio G. Cota
  2009-08-10 13:13         ` Martyn Welch
  2009-08-10 16:28         ` Greg KH
  0 siblings, 2 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-09 12:17 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

(oops, just correcting Sebastien's e-mail address)

Emilio G. Cota wrote:
> I remember we had a look at the vmelinux interface, but it really
> was so limited that we decided to get rid of it altogether. Do you
> really think it's worth bringing it back to life? Since it's never
> been merged into the kernel, why don't we just get it right this time
> and forget about it?
> 
> Instead of using that we implemented a heretic IOCTL-based
> interface for user-space; at least with it you could create a
> driver (with no interrupt support) for a device in user-space.
> We actually don't use it, but well, just to get inspiration, here it
> goes:
> 
> /**
>  * \name Window management ioctl numbers
>  */
> /** Get a physical window attributes */
> #define VME_IOCTL_GET_WINDOW_ATTR       _IOWR('V', 0, struct
> vme_mapping)
> /** Create a physical window */
> #define VME_IOCTL_CREATE_WINDOW         _IOW( 'V', 1, struct
> vme_mapping)
> /** Destroy a physical window */
> #define VME_IOCTL_DESTROY_WINDOW        _IOW( 'V', 2, int)
> /** Create a mapping over a physical window */
> #define VME_IOCTL_FIND_MAPPING          _IOWR('V', 3, struct
> vme_mapping)
> /** Remove a mapping */
> #define VME_IOCTL_RELEASE_MAPPING       _IOW( 'V', 4, struct
> vme_mapping)
> /** Get the create on find failed flag */
> #define VME_IOCTL_GET_CREATE_ON_FIND_FAIL       _IOR( 'V', 5, unsigned
> int)
> /** Set the create on find failed flag */
> #define VME_IOCTL_SET_CREATE_ON_FIND_FAIL       _IOW( 'V', 6, unsigned
> int)
> /** Get the destroy on remove flag */
> #define VME_IOCTL_GET_DESTROY_ON_REMOVE _IOR( 'V', 7, unsigned int)
> /** Set the destroy on remove flag */
> #define VME_IOCTL_SET_DESTROY_ON_REMOVE _IOW( 'V', 8, unsigned int)
> /** Get bus error status */
> #define VME_IOCTL_GET_BUS_ERROR         _IOR( 'V', 9, unsigned int)
> /** Start a DMA transfer */
> #define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)
> 
> [ grab the code from here: http://repo.or.cz/w/tsi148vmebridge.git .
>   Please note there's some backwards-compatible crap for old user-space
>   LynxOS code, called CES. Ignore it. ]
> 
> By the way, please try to be consistent with naming, e.g. I see window,
> then see "image", and I have to think hard to see whether they mean
> the same or not.. err, do they?
> 
> E.
> 
> (I'll leave this here because of added Cc)
> Greg K-H wrote:
> > From: Martyn Welch <martyn.welch@gefanuc.com>
> > 
> > Adds a VME userspace access driver
> > 
> > Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
> > Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
> > 
> > ---
> >  drivers/staging/vme/Kconfig            |    4 
> >  drivers/staging/vme/Makefile           |    2 
> >  drivers/staging/vme/devices/Kconfig    |    8 
> >  drivers/staging/vme/devices/Makefile   |    5 
> >  drivers/staging/vme/devices/vme_user.c |  771 +++++++++++++++++++++++++++++++++
> >  drivers/staging/vme/devices/vme_user.h |   43 +
> >  6 files changed, 830 insertions(+), 3 deletions(-)
> > 
> > --- /dev/null
> > +++ b/drivers/staging/vme/devices/Kconfig
> > @@ -0,0 +1,8 @@
> > +comment "VME Device Drivers"
> > +
> > +config VME_USER
> > +	tristate "VME user space access driver"
> > +	help
> > +	  If you say Y here you want to be able to access a limited number of
> > +	  VME windows in a manner at least semi-compatible with the interface
> > +	  provided with the original driver at http://vmelinux.org/.
> > --- /dev/null
> > +++ b/drivers/staging/vme/devices/Makefile
> > @@ -0,0 +1,5 @@
> > +#
> > +# Makefile for the VME device drivers.
> > +#
> > +
> > +obj-$(CONFIG_VME_USER)		+= vme_user.o
> > --- /dev/null
> > +++ b/drivers/staging/vme/devices/vme_user.c
> > @@ -0,0 +1,771 @@
> > +/*
> > + * VMEbus User access driver
> > + *
> > + * Author: Martyn Welch <martyn.welch@gefanuc.com>
> > + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
> > + *
> > + * Based on work by:
> > + *   Tom Armistead and Ajit Prem
> > + *     Copyright 2004 Motorola Inc.
> > + *
> > + *
> > + * This program is free software; you can redistribute  it and/or modify it
> > + * under  the terms of  the GNU General  Public License as published by the
> > + * Free Software Foundation;  either version 2 of the  License, or (at your
> > + * option) any later version.
> > + */
> > +
> > +#include <linux/cdev.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/errno.h>
> > +#include <linux/init.h>
> > +#include <linux/ioctl.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/pagemap.h>
> > +#include <linux/pci.h>
> > +#include <linux/semaphore.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/syscalls.h>
> > +#include <linux/types.h>
> > +#include <linux/version.h>
> > +
> > +#include <asm/io.h>
> > +#include <asm/uaccess.h>
> > +
> > +#include "../vme.h"
> > +#include "vme_user.h"
> > +
> > +/* Currently Documentation/devices.txt defines the following for VME:
> > + *
> > + * 221 char	VME bus
> > + * 		  0 = /dev/bus/vme/m0		First master image
> > + * 		  1 = /dev/bus/vme/m1		Second master image
> > + * 		  2 = /dev/bus/vme/m2		Third master image
> > + * 		  3 = /dev/bus/vme/m3		Fourth master image
> > + * 		  4 = /dev/bus/vme/s0		First slave image
> > + * 		  5 = /dev/bus/vme/s1		Second slave image
> > + * 		  6 = /dev/bus/vme/s2		Third slave image
> > + * 		  7 = /dev/bus/vme/s3		Fourth slave image
> > + * 		  8 = /dev/bus/vme/ctl		Control
> > + *
> > + * 		It is expected that all VME bus drivers will use the
> > + * 		same interface.  For interface documentation see
> > + * 		http://www.vmelinux.org/.
> > + *
> > + * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
> > + * even support the tsi148 chipset (which has 8 master and 8 slave windows).
> > + * We'll run with this or now as far as possible, however it probably makes
> > + * sense to get rid of the old mappings and just do everything dynamically.
> > + *
> > + * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
> > + * defined above and try to support at least some of the interface from
> > + * http://www.vmelinux.org/ as an alternative drive can be written providing a
> > + * saner interface later.
> > + */
> > +#define VME_MAJOR	221	/* VME Major Device Number */
> > +#define VME_DEVS	9	/* Number of dev entries */
> > +
> > +#define MASTER_MINOR	0
> > +#define MASTER_MAX	3
> > +#define SLAVE_MINOR	4
> > +#define SLAVE_MAX	7
> > +#define CONTROL_MINOR	8
> > +
> > +#define PCI_BUF_SIZE  0x20000	/* Size of one slave image buffer */
> > +
> > +/*
> > + * Structure to handle image related parameters.
> > + */
> > +typedef struct {
> > +	void __iomem *kern_buf;	/* Buffer address in kernel space */
> > +	dma_addr_t pci_buf;	/* Buffer address in PCI address space */
> > +	unsigned long long size_buf;	/* Buffer size */
> > +	struct semaphore sem;	/* Semaphore for locking image */
> > +	struct device *device;	/* Sysfs device */
> > +	struct vme_resource *resource;	/* VME resource */
> > +	int users;		/* Number of current users */
> > +} image_desc_t;
> > +static image_desc_t image[VME_DEVS];
> > +
> > +typedef struct {
> > +	unsigned long reads;
> > +	unsigned long writes;
> > +	unsigned long ioctls;
> > +	unsigned long irqs;
> > +	unsigned long berrs;
> > +	unsigned long dmaErrors;
> > +	unsigned long timeouts;
> > +	unsigned long external;
> > +} driver_stats_t;
> > +static driver_stats_t statistics;
> > +
> > +struct cdev *vme_user_cdev;		/* Character device */
> > +struct class *vme_user_sysfs_class;	/* Sysfs class */
> > +struct device *vme_user_bridge;		/* Pointer to the bridge device */
> > +
> > +static char driver_name[] = "vme_user";
> > +
> > +static const int type[VME_DEVS] = {	MASTER_MINOR,	MASTER_MINOR,
> > +					MASTER_MINOR,	MASTER_MINOR,
> > +					SLAVE_MINOR,	SLAVE_MINOR,
> > +					SLAVE_MINOR,	SLAVE_MINOR,
> > +					CONTROL_MINOR
> > +				};
> > +
> > +
> > +static int vme_user_open(struct inode *, struct file *);
> > +static int vme_user_release(struct inode *, struct file *);
> > +static ssize_t vme_user_read(struct file *, char *, size_t, loff_t *);
> > +static ssize_t vme_user_write(struct file *, const char *, size_t, loff_t *);
> > +static loff_t vme_user_llseek(struct file *, loff_t, int);
> > +static int vme_user_ioctl(struct inode *, struct file *, unsigned int,
> > +	unsigned long);
> > +
> > +static int __init vme_user_probe(struct device *dev);
> > +
> > +static struct file_operations vme_user_fops = {
> > +        .open = vme_user_open,
> > +        .release = vme_user_release,
> > +        .read = vme_user_read,
> > +        .write = vme_user_write,
> > +        .llseek = vme_user_llseek,
> > +        .ioctl = vme_user_ioctl,
> > +};
> > +
> > +
> > +/*
> > + * Reset all the statistic counters
> > + */
> > +static void reset_counters(void)
> > +{
> > +        statistics.reads = 0;
> > +        statistics.writes = 0;
> > +        statistics.ioctls = 0;
> > +        statistics.irqs = 0;
> > +        statistics.berrs = 0;
> > +        statistics.dmaErrors = 0;
> > +        statistics.timeouts = 0;
> > +}
> > +
> > +void lmcall(int monitor)
> > +{
> > +	printk("Caught Location Monitor %d access\n", monitor);
> > +}
> > +
> > +static void tests(void)
> > +{
> > +	struct vme_resource *dma_res;
> > +	struct vme_dma_list *dma_list;
> > +	struct vme_dma_attr *pattern_attr, *vme_attr;
> > +
> > +	int retval;
> > +	unsigned int data;
> > +
> > +	printk("Running VME DMA test\n");
> > +	dma_res = vme_request_dma(vme_user_bridge);
> > +	dma_list = vme_new_dma_list(dma_res);
> > +	pattern_attr = vme_dma_pattern_attribute(0x0,
> > +		VME_DMA_PATTERN_WORD |
> > +			VME_DMA_PATTERN_INCREMENT);
> > +	vme_attr = vme_dma_vme_attribute(0x10000, VME_A32,
> > +		VME_SCT, VME_D32);
> > +	retval = vme_dma_list_add(dma_list, pattern_attr,
> > +		vme_attr, 0x10000);
> > +#if 0
> > +	vme_dma_free_attribute(vme_attr);
> > +	vme_attr = vme_dma_vme_attribute(0x20000, VME_A32,
> > +		VME_SCT, VME_D32);
> > +	retval = vme_dma_list_add(dma_list, pattern_attr,
> > +		vme_attr, 0x10000);
> > +#endif
> > +	retval = vme_dma_list_exec(dma_list);
> > +	vme_dma_free_attribute(pattern_attr);
> > +	vme_dma_free_attribute(vme_attr);
> > +	vme_dma_list_free(dma_list);
> > +#if 0
> > +	printk("Generating a VME interrupt\n");
> > +	vme_generate_irq(dma_res, 0x3, 0xaa);
> > +	printk("Interrupt returned\n");
> > +#endif
> > +	vme_dma_free(dma_res);
> > +
> > +	/* Attempt RMW */
> > +	data = vme_master_rmw(image[0].resource, 0x80000000, 0x00000000,
> > +		0x80000000, 0);
> > +	printk("RMW returned 0x%8.8x\n", data);
> > +
> > +
> > +	/* Location Monitor */
> > +	printk("vme_lm_set:%d\n", vme_lm_set(vme_user_bridge, 0x60000, VME_A32, VME_SCT | VME_USER | VME_DATA));
> > +	printk("vme_lm_attach:%d\n", vme_lm_attach(vme_user_bridge, 0, lmcall));
> > +
> > +	printk("Board in VME slot:%d\n", vme_slot_get(vme_user_bridge));
> > +}
> > +
> > +static int vme_user_open(struct inode *inode, struct file *file)
> > +{
> > +	int err;
> > +	unsigned int minor = MINOR(inode->i_rdev);
> > +
> > +	down(&image[minor].sem);
> > +	/* Only allow device to be opened if a resource is allocated */
> > +	if (image[minor].resource == NULL) {
> > +		printk(KERN_ERR "No resources allocated for device\n");
> > +		err = -EINVAL;
> > +		goto err_res;
> > +	}
> > +
> > +	/* Increment user count */
> > +	image[minor].users++;
> > +
> > +	up(&image[minor].sem);
> > +
> > +	return 0;
> > +
> > +err_res:
> > +	up(&image[minor].sem);
> > +
> > +	return err;
> > +}
> > +
> > +static int vme_user_release(struct inode *inode, struct file *file)
> > +{
> > +	unsigned int minor = MINOR(inode->i_rdev);
> > +
> > +	down(&image[minor].sem);
> > +
> > +	/* Decrement user count */
> > +	image[minor].users--;
> > +
> > +	up(&image[minor].sem);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * We are going ot alloc a page during init per window for small transfers.
> > + * Small transfers will go VME -> buffer -> user space. Larger (more than a
> > + * page) transfers will lock the user space buffer into memory and then
> > + * transfer the data directly into the user space buffers.
> > + */
> > +static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
> > +	loff_t *ppos)
> > +{
> > +	ssize_t retval;
> > +	ssize_t copied = 0;
> > +
> > +	if (count <= image[minor].size_buf) {
> > +		/* We copy to kernel buffer */
> > +		copied = vme_master_read(image[minor].resource,
> > +			image[minor].kern_buf, count, *ppos);
> > +		if (copied < 0) {
> > +			return (int)copied;
> > +		}
> > +
> > +		retval = __copy_to_user(buf, image[minor].kern_buf,
> > +			(unsigned long)copied);
> > +		if (retval != 0) {
> > +			copied = (copied - retval);
> > +			printk("User copy failed\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +	} else {
> > +		/* XXX Need to write this */
> > +		printk("Currently don't support large transfers\n");
> > +		/* Map in pages from userspace */
> > +
> > +		/* Call vme_master_read to do the transfer */
> > +		return -EINVAL;
> > +	}
> > +
> > +	return copied;
> > +}
> > +
> > +/*
> > + * We are going ot alloc a page during init per window for small transfers.
> > + * Small transfers will go user space -> buffer -> VME. Larger (more than a
> > + * page) transfers will lock the user space buffer into memory and then
> > + * transfer the data directly from the user space buffers out to VME.
> > + */
> > +static ssize_t resource_from_user(unsigned int minor, const char *buf,
> > +	size_t count, loff_t *ppos)
> > +{
> > +	ssize_t retval;
> > +	ssize_t copied = 0;
> > +
> > +	if (count <= image[minor].size_buf) {
> > +		retval = __copy_from_user(image[minor].kern_buf, buf,
> > +			(unsigned long)count);
> > +		if (retval != 0)
> > +			copied = (copied - retval);
> > +		else
> > +			copied = count;
> > +
> > +		copied = vme_master_write(image[minor].resource,
> > +			image[minor].kern_buf, copied, *ppos);
> > +	} else {
> > +		/* XXX Need to write this */
> > +		printk("Currently don't support large transfers\n");
> > +		/* Map in pages from userspace */
> > +
> > +		/* Call vme_master_write to do the transfer */
> > +		return -EINVAL;
> > +	}
> > +
> > +	return copied;
> > +}
> > +
> > +static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
> > +	size_t count, loff_t *ppos)
> > +{
> > +	void __iomem *image_ptr;
> > +	ssize_t retval;
> > +
> > +	image_ptr = image[minor].kern_buf + *ppos;
> > +
> > +	retval = __copy_to_user(buf, image_ptr, (unsigned long)count);
> > +	if (retval != 0) {
> > +		retval = (count - retval);
> > +		printk(KERN_WARNING "Partial copy to userspace\n");
> > +	} else
> > +		retval = count;
> > +
> > +	/* Return number of bytes successfully read */
> > +	return retval;
> > +}
> > +
> > +static ssize_t buffer_from_user(unsigned int minor, const char *buf,
> > +	size_t count, loff_t *ppos)
> > +{
> > +	void __iomem *image_ptr;
> > +	size_t retval;
> > +
> > +	image_ptr = image[minor].kern_buf + *ppos;
> > +
> > +	retval = __copy_from_user(image_ptr, buf, (unsigned long)count);
> > +	if (retval != 0) {
> > +		retval = (count - retval);
> > +		printk(KERN_WARNING "Partial copy to userspace\n");
> > +	} else
> > +		retval = count;
> > +
> > +	/* Return number of bytes successfully read */
> > +	return retval;
> > +}
> > +
> > +static ssize_t vme_user_read(struct file *file, char *buf, size_t count,
> > +			loff_t * ppos)
> > +{
> > +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
> > +	ssize_t retval;
> > +	size_t image_size;
> > +	size_t okcount;
> > +
> > +	down(&image[minor].sem);
> > +
> > +	/* XXX Do we *really* want this helper - we can use vme_*_get ? */
> > +	image_size = vme_get_size(image[minor].resource);
> > +
> > +	/* Ensure we are starting at a valid location */
> > +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
> > +		up(&image[minor].sem);
> > +		return 0;
> > +	}
> > +
> > +	/* Ensure not reading past end of the image */
> > +	if (*ppos + count > image_size)
> > +		okcount = image_size - *ppos;
> > +	else
> > +		okcount = count;
> > +
> > +	switch (type[minor]){
> > +	case MASTER_MINOR:
> > +		retval = resource_to_user(minor, buf, okcount, ppos);
> > +		break;
> > +	case SLAVE_MINOR:
> > +		retval = buffer_to_user(minor, buf, okcount, ppos);
> > +		break;
> > +	default:
> > +		retval = -EINVAL;
> > +	}
> > +
> > +	up(&image[minor].sem);
> > +
> > +	if (retval > 0)
> > +		*ppos += retval;
> > +
> > +	return retval;
> > +}
> > +
> > +static ssize_t vme_user_write(struct file *file, const char *buf, size_t count,
> > +			 loff_t *ppos)
> > +{
> > +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
> > +	ssize_t retval;
> > +	size_t image_size;
> > +	size_t okcount;
> > +
> > +	down(&image[minor].sem);
> > +
> > +	image_size = vme_get_size(image[minor].resource);
> > +
> > +	/* Ensure we are starting at a valid location */
> > +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
> > +		up(&image[minor].sem);
> > +		return 0;
> > +	}
> > +
> > +	/* Ensure not reading past end of the image */
> > +	if (*ppos + count > image_size)
> > +		okcount = image_size - *ppos;
> > +	else
> > +		okcount = count;
> > +
> > +	switch (type[minor]){
> > +	case MASTER_MINOR:
> > +		retval = resource_from_user(minor, buf, okcount, ppos);
> > +		break;
> > +	case SLAVE_MINOR:
> > +		retval = buffer_from_user(minor, buf, okcount, ppos);
> > +		break;
> > +	default:
> > +		retval = -EINVAL;
> > +	}
> > +
> > +	up(&image[minor].sem);
> > +
> > +	if (retval > 0)
> > +		*ppos += retval;
> > +
> > +	return retval;
> > +}
> > +
> > +static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
> > +{
> > +	printk(KERN_ERR "Llseek currently incomplete\n");
> > +	return -EINVAL;
> > +}
> > +
> > +static int vme_user_ioctl(struct inode *inode, struct file *file,
> > +	unsigned int cmd, unsigned long arg)
> > +{
> > +	unsigned int minor = MINOR(inode->i_rdev);
> > +#if 0
> > +	int ret_val;
> > +#endif
> > +	unsigned long copyRet;
> > +	vme_slave_t slave;
> > +
> > +	statistics.ioctls++;
> > +	switch (type[minor]) {
> > +	case CONTROL_MINOR:
> > +		break;
> > +	case MASTER_MINOR:
> > +		break;
> > +	case SLAVE_MINOR:
> > +		switch (cmd) {
> > +		case VME_SET_SLAVE:
> > +
> > +			copyRet = copy_from_user(&slave, (char *)arg,
> > +				sizeof(slave));
> > +			if (copyRet != 0) {
> > +				printk(KERN_WARNING "Partial copy from "
> > +					"userspace\n");
> > +				return -EFAULT;
> > +			}
> > +
> > +			return vme_slave_set(image[minor].resource,
> > +				slave.enable, slave.vme_addr, slave.size,
> > +				image[minor].pci_buf, slave.aspace,
> > +				slave.cycle);
> > +
> > +			break;
> > +#if 0
> > +		case VME_GET_SLAVE:
> > +			vme_slave_t slave;
> > +
> > +			ret_val = vme_slave_get(minor, &iRegs);
> > +
> > +			copyRet = copy_to_user((char *)arg, &slave,
> > +				sizeof(slave));
> > +			if (copyRet != 0) {
> > +				printk(KERN_WARNING "Partial copy to "
> > +					"userspace\n");
> > +				return -EFAULT;
> > +			}
> > +
> > +			return ret_val;
> > +			break;
> > +#endif
> > +		}
> > +		break;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +
> > +/*
> > + * Unallocate a previously allocated buffer
> > + */
> > +static void buf_unalloc (int num)
> > +{
> > +	if (image[num].kern_buf) {
> > +#ifdef VME_DEBUG
> > +		printk(KERN_DEBUG "UniverseII:Releasing buffer at %p\n",
> > +			image[num].pci_buf);
> > +#endif
> > +
> > +		vme_free_consistent(image[num].resource, image[num].size_buf,
> > +			image[num].kern_buf, image[num].pci_buf);
> > +
> > +		image[num].kern_buf = NULL;
> > +		image[num].pci_buf = 0;
> > +		image[num].size_buf = 0;
> > +
> > +#ifdef VME_DEBUG
> > +	} else {
> > +		printk(KERN_DEBUG "UniverseII: Buffer not allocated\n");
> > +#endif
> > +	}
> > +}
> > +
> > +static struct vme_driver vme_user_driver = {
> > +        .name = driver_name,
> > +        .probe = vme_user_probe,
> > +};
> > +
> > +
> > +/*
> > + * In this simple access driver, the old behaviour is being preserved as much
> > + * as practical. We will therefore reserve the buffers and request the images
> > + * here so that we don't have to do it later.
> > + */
> > +static int __init vme_bridge_init(void)
> > +{
> > +	int retval;
> > +	printk(KERN_INFO "VME User Space Access Driver\n");
> > +	printk("vme_user_driver:%p\n", &vme_user_driver);
> > +	retval = vme_register_driver(&vme_user_driver);
> > +	printk("vme_register_driver returned %d\n", retval);
> > +	return retval;
> > +}
> > +
> > +/*
> > + * This structure gets passed a device, this should be the device created at
> > + * registration.
> > + */
> > +static int __init vme_user_probe(struct device *dev)
> > +{
> > +	int i, err;
> > +	char name[8];
> > +
> > +	printk("Running vme_user_probe()\n");
> > +
> > +	/* Pointer to the bridge device */
> > +	vme_user_bridge = dev;
> > +
> > +	/* Initialise descriptors */
> > +	for (i = 0; i < VME_DEVS; i++) {
> > +		image[i].kern_buf = NULL;
> > +		image[i].pci_buf = 0;
> > +		init_MUTEX(&(image[i].sem));
> > +		image[i].device = NULL;
> > +		image[i].resource = NULL;
> > +		image[i].users = 0;
> > +	}
> > +
> > +	/* Initialise statistics counters */
> > +	reset_counters();
> > +
> > +	/* Assign major and minor numbers for the driver */
> > +	err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS,
> > +		driver_name);
> > +	if (err) {
> > +		printk(KERN_WARNING "%s: Error getting Major Number %d for "
> > +		"driver.\n", driver_name, VME_MAJOR);
> > +		goto err_region;
> > +	}
> > +
> > +	/* Register the driver as a char device */
> > +	vme_user_cdev = cdev_alloc();
> > +	vme_user_cdev->ops = &vme_user_fops;
> > +	vme_user_cdev->owner = THIS_MODULE;
> > +	err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
> > +	if (err) {
> > +		printk(KERN_WARNING "%s: cdev_all failed\n", driver_name);
> > +		goto err_char;
> > +	}
> > +
> > +	/* Request slave resources and allocate buffers (128kB wide) */
> > +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> > +		/* XXX Need to properly request attributes */
> > +		image[i].resource = vme_slave_request(vme_user_bridge,
> > +			VME_A16, VME_SCT);
> > +		if (image[i].resource == NULL) {
> > +			printk(KERN_WARNING "Unable to allocate slave "
> > +				"resource\n");
> > +			goto err_buf;
> > +		}
> > +		image[i].size_buf = PCI_BUF_SIZE;
> > +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
> > +			image[i].size_buf, &(image[i].pci_buf));
> > +		if (image[i].kern_buf == NULL) {
> > +			printk(KERN_WARNING "Unable to allocate memory for "
> > +				"buffer\n");
> > +			image[i].pci_buf = 0;
> > +			vme_slave_free(image[i].resource);
> > +			err = -ENOMEM;
> > +			goto err_buf;
> > +		}
> > +	}
> > +
> > +	/*
> > +	 * Request master resources allocate page sized buffers for small
> > +	 * reads and writes
> > +	 */
> > +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
> > +		/* XXX Need to properly request attributes */
> > +		image[i].resource = vme_master_request(vme_user_bridge,
> > +			VME_A32, VME_SCT, VME_D32);
> > +		if (image[i].resource == NULL) {
> > +			printk(KERN_WARNING "Unable to allocate master "
> > +				"resource\n");
> > +			goto err_buf;
> > +		}
> > +		image[i].size_buf = PAGE_SIZE;
> > +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
> > +			image[i].size_buf, &(image[i].pci_buf));
> > +		if (image[i].kern_buf == NULL) {
> > +			printk(KERN_WARNING "Unable to allocate memory for "
> > +				"buffer\n");
> > +			image[i].pci_buf = 0;
> > +			vme_master_free(image[i].resource);
> > +			err = -ENOMEM;
> > +			goto err_buf;
> > +		}
> > +	}
> > +
> > +	/* Setup some debug windows */
> > +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> > +		err = vme_slave_set(image[i].resource, 1, 0x4000*(i-4),
> > +			0x4000, image[i].pci_buf, VME_A16,
> > +			VME_SCT | VME_SUPER | VME_USER | VME_PROG | VME_DATA);
> > +		if (err != 0) {
> > +			printk(KERN_WARNING "Failed to configure window\n");
> > +			goto err_buf;
> > +		}
> > +	}
> > +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
> > +		err = vme_master_set(image[i].resource, 1,
> > +			(0x10000 + (0x10000*i)), 0x10000,
> > +			VME_A32, VME_SCT | VME_USER | VME_DATA, VME_D32);
> > +		if (err != 0) {
> > +			printk(KERN_WARNING "Failed to configure window\n");
> > +			goto err_buf;
> > +		}
> > +	}
> > +
> > +	/* Create sysfs entries - on udev systems this creates the dev files */
> > +	vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
> > +	if (IS_ERR(vme_user_sysfs_class)) {
> > +		printk(KERN_ERR "Error creating vme_user class.\n");
> > +		err = PTR_ERR(vme_user_sysfs_class);
> > +		goto err_class;
> > +	}
> > +
> > +	/* Add sysfs Entries */
> > +	for (i=0; i<VME_DEVS; i++) {
> > +		switch (type[i]) {
> > +		case MASTER_MINOR:
> > +			sprintf(name,"bus/vme/m%%d");
> > +			break;
> > +		case CONTROL_MINOR:
> > +			sprintf(name,"bus/vme/ctl");
> > +			break;
> > +		case SLAVE_MINOR:
> > +			sprintf(name,"bus/vme/s%%d");
> > +			break;
> > +		default:
> > +			err = -EINVAL;
> > +			goto err_sysfs;
> > +			break;
> > +		}
> > +
> > +		image[i].device =
> > +			device_create(vme_user_sysfs_class, NULL,
> > +				MKDEV(VME_MAJOR, i), NULL, name,
> > +				(type[i] == SLAVE_MINOR)? i - (MASTER_MAX + 1) : i);
> > +		if (IS_ERR(image[i].device)) {
> > +			printk("%s: Error creating sysfs device\n",
> > +				driver_name);
> > +			err = PTR_ERR(image[i].device);
> > +			goto err_sysfs;
> > +		}
> > +	}
> > +
> > +	/* XXX Run tests */
> > +	/*
> > +	tests();
> > +	*/
> > +
> > +	return 0;
> > +
> > +	/* Ensure counter set correcty to destroy all sysfs devices */
> > +	i = VME_DEVS;
> > +err_sysfs:
> > +	while (i > 0){
> > +		i--;
> > +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
> > +	}
> > +	class_destroy(vme_user_sysfs_class);
> > +
> > +	/* Ensure counter set correcty to unalloc all slave buffers */
> > +	i = SLAVE_MAX + 1;
> > +err_buf:
> > +	while (i > SLAVE_MINOR){
> > +		i--;
> > +		vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
> > +		vme_slave_free(image[i].resource);
> > +		buf_unalloc(i);
> > +	}
> > +err_class:
> > +	cdev_del(vme_user_cdev);
> > +err_char:
> > +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
> > +err_region:
> > +	return err;
> > +}
> > +
> > +static void __exit vme_bridge_exit(void)
> > +{
> > +	int i;
> > +
> > +	/* Remove sysfs Entries */
> > +	for(i=0; i<VME_DEVS; i++) {
> > +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
> > +	}
> > +	class_destroy(vme_user_sysfs_class);
> > +
> > +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
> > +		buf_unalloc(i);
> > +	}
> > +
> > +	/* Unregister device driver */
> > +	cdev_del(vme_user_cdev);
> > +
> > +	/* Unregiser the major and minor device numbers */
> > +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
> > +}
> > +
> > +MODULE_DESCRIPTION("VME User Space Access Driver");
> > +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
> > +MODULE_LICENSE("GPL");
> > +
> > +module_init(vme_bridge_init);
> > +module_exit(vme_bridge_exit);
> > --- /dev/null
> > +++ b/drivers/staging/vme/devices/vme_user.h
> > @@ -0,0 +1,43 @@
> > +
> > +/*
> > + * VMEbus Master Window Configuration Structure
> > + */
> > +typedef struct {
> > +	char enable;			/* State of Window */
> > +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
> > +	unsigned long long size;	/* Window Size */
> > +	vme_address_t aspace;		/* Address Space */
> > +	vme_cycle_t cycle;		/* Cycle properties */
> > +	vme_width_t dwidth;		/* Maximum Data Width */
> > +#if 0
> > +	char prefetchEnable;		/* Prefetch Read Enable State */
> > +	int prefetchSize;		/* Prefetch Read Size (Cache Lines) */
> > +	char wrPostEnable;		/* Write Post State */
> > +#endif
> > +} vme_master_t;
> > +
> > +
> > +/*
> > + * IOCTL Commands and structures
> > + */
> > +
> > +/* Magic number for use in ioctls */
> > +#define VME_IOC_MAGIC 0xAE
> > +
> > +
> > +/* VMEbus Slave Window Configuration Structure */
> > +typedef struct {
> > +	char enable;			/* State of Window */
> > +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
> > +	unsigned long long size;	/* Window Size */
> > +	vme_address_t aspace;		/* Address Space */
> > +	vme_cycle_t cycle;		/* Cycle properties */
> > +#if 0
> > +	char wrPostEnable;		/* Write Post State */
> > +	char rmwLock;			/* Lock PCI during RMW Cycles */
> > +	char data64BitCapable;		/* non-VMEbus capable of 64-bit Data */
> > +#endif
> > +} vme_slave_t;
> > +
> > +#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 10, vme_slave_t)
> > +
> > --- a/drivers/staging/vme/Kconfig
> > +++ b/drivers/staging/vme/Kconfig
> > @@ -11,7 +11,7 @@ menuconfig VME
> >  if VME
> >  
> >  #source "drivers/staging/vme/bridges/Kconfig"
> > -#
> > -#source "drivers/staging/vme/devices/Kconfig"
> > +
> > +source "drivers/staging/vme/devices/Kconfig"
> >  
> >  endif # VME
> > --- a/drivers/staging/vme/Makefile
> > +++ b/drivers/staging/vme/Makefile
> > @@ -4,4 +4,4 @@
> >  obj-$(CONFIG_VME)		+= vme.o
> >  
> >  #obj-y				+= bridges/
> > -#obj-y				+= devices/
> > +obj-y				+= devices/
> > 
> > 
> 

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-08 23:01     ` Emilio G. Cota
@ 2009-08-10 12:44       ` Martyn Welch
  2009-08-10 14:14         ` Emilio G. Cota
  2009-08-11  4:54         ` Mike Frysinger
  2009-08-10 16:30       ` Greg KH
  1 sibling, 2 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 12:44 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Hi Emilio,

Emilio G. Cota wrote:
> A few comments after a quick glance, will need to dig deeper
> though:
> - semaphores? isn't it screaming for mutexes?
>   

The semaphores are initialized in mutex mode.

> - some function signatures (get_whatever) pass too many
>   pointers; it's rather ugly. Passing a pointer to an
>   appropriate struct seems a better solution.
>   

When implementing these functions it seemed like a simpler option to 
pass the parameters rather than defining yet another structure and using 
it for a single call. If the *_get functions are changed then the *_set 
functions should also be changed for consistency. I'm not overly 
bothered either way - if the consensus is that a structure would be 
better then we can go with a structure.

> - vme_alloc_consistent looks pretty fishy; why don't you pass
>   that responsibility to the specific master? There you obviously
>   know if you're bridging over PCI or whatever. Or even better;
>   why is this needed at all?
>   

In the model I have chosen it is up to the VME device driver to create 
buffers rather than the VME bridge drivers. After all it is the device 
drivers for the specific hardware found on the VME bus that knows what 
it is going to do with these buffers. This method is provided as a 
helper. It ensures that a VME device driver (not to be confused with the 
VME bridge driver) is able to allocate a buffer suitable for utilizing 
the DMA engines found in the bridge or indeed to be mapped onto VME 
though a slave window without explicitly knowing which VME bridge is 
being used.

Master windows don't require a contiguous buffer, are you referring to 
something else when you say master?

> - Please explain me what all the DMA functions do; are they
>   meant to be used by master or slaves?
>   
The TODO file (drivers/staging/vme/TODO) contains a description of the 
current API including the DMA functions, does that provide enough of a 
description?

I am not aware of a VME bridge where the DMA controllers are 
specifically linked to the master or slave windows, they operate 
independently of either though during DMA operation are acting as bus 
master. In the tsi-148 the DMA transfers can use PCI space both as a 
source and destination, so I guess you could use them to transfer data 
into the slave buffer. Other chipsets, such as the ca91c142 can't do 
this, the DMA engines can only transfer PCI-to-VME or VME-to_PCI.

> Have a look at the interface for slaves we've got for our tsi148
> driver, available at:
> http://repo.or.cz/w/tsi148vmebridge.git
>
> /* API for new drivers */
> extern int vme_request_irq(unsigned int, int (*)(void *),
>                            void *, const char *);
> extern int vme_free_irq(unsigned int );
> extern int vme_generate_interrupt(int, int, signed long);
> extern struct vme_mapping* find_vme_mapping_from_addr(unsigned);
> extern int vme_get_window_attr(struct vme_mapping *);
> extern int vme_create_window(struct vme_mapping *);
> extern int vme_destroy_window(int);
> extern int vme_find_mapping(struct vme_mapping *, int);
> extern int vme_release_mapping(struct vme_mapping *, int);
> extern int vme_do_dma(struct vme_dma *);
> extern int vme_bus_error_check(int);
>
> That's pretty thin and it covers our slaves' needs. Do you see
> anything missing there?
>   

This interface, especially struct vme_mapping seems to have been written 
solely for the tsi-148 bridge. The API I have defined aims to provide a 
consistent API across multiple VME bridges. Whilst this API clearly 
works for you with the tsi-148 I am unsure how suitable it will be for 
other bridge chips.

The API I have proposed is designed to support more than just the tsi148 
chipset. Have you thought about how the above API will be supported on 
other VME bridges?

> For masters there's no interface there because it was the
> master's driver who directly provided these calls to slaves.
> I had it in my to-do list to split that from the tsi148, in
> the same fashion as you've done with this work.
>
> - Note above the interrupt handler; simply needs the cookie. Also,
>   shouldn't your vme_request_irq() just require the IRQ vector?
>   

No. I believe that you are using the term IRQ vector where we would use 
the term status ID, the value which is returned from an IACK cycle. Your 
interrupt handling code assigns a single interrupt handler to all 
interrupt levels, purely using the interrupt vector/status ID to 
determine which interrupt handler will be used. This adds an artificial 
limitation and would not work in some instances that we have seen. Our 
framework provides the ability to attach an interrupt handler to each 
combination of IRQ level and Status ID/vector.

> - I'd like to see the whole picture, or 'vertical slice', i.e.
>   the bus interface + a master + a slave driver. How would
>   the slave's driver get the addresses and sizes of the mappings,
>   interrupt lines, etc. for each of the devices it controls?
>   For the time being we quickly hacked an xml-based scheme to get
>   this info upon installation, but it's clearly not suitable
>   for mainline.
>   
I have written a test driver for a very old slave card we had lying 
around. In that case we used module parameters to determine the location 
of the device on the bus (it used a fixed VME address space). In this 
instance the interrupt level and ID could be configured in the registers 
available in the VME address space, hence I added module parameters to 
allow these to be configured. In this respect configuration of VME 
devices is very similar to ISA devices - neither of the buses has 
supported discovery mechanism from the outset and thus old cards. I 
therefore implemented a mechanism similar to how I believe ISA 
approaches this.

The framework that I have proposed aims to provide a consistent API to 
manage allowing the resources provided by the VME bridges to be managed 
in as a consistent a manner as possible. I believe it is up to the 
device drivers for the devices found on the VME bus to determine the 
best way to configure this as it is not provided by the VME specifications.

For using the VME bus for communication between two SBCs, I think we 
could use the CRCSR space to provide some information about the 
resources used on each board for say, a virtual network driver like the 
rapidIO subsystem provides. Possibly using the "device tree" stuff used 
on PowerPC, Blackfin and Sparc archs (I believe) for passing device 
layout between the firmware and kernel at boot.
> Will probably have more comments when I get some time for further
> inspection.
>
>   
Sure, np,

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-09 12:17       ` Emilio G. Cota
@ 2009-08-10 13:13         ` Martyn Welch
  2009-08-10 15:26           ` Emilio G. Cota
  2009-08-10 16:28         ` Greg KH
  1 sibling, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 13:13 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> (oops, just correcting Sebastien's e-mail address)
>
> Emilio G. Cota wrote:
>   
>> I remember we had a look at the vmelinux interface, but it really
>> was so limited that we decided to get rid of it altogether. Do you
>> really think it's worth bringing it back to life? Since it's never
>> been merged into the kernel, why don't we just get it right this time
>> and forget about it?
>>     

I'm happy to forget about it. I've come to the conclusion that the 
IOCTLs are unmaintainable anyway. This patch at the moment is acting as 
a useful tool for exercising the underlying layers and provides a bit of 
example code (or will as the patches I have recently sent to the Linux 
Driver Project propagate through).

>> Instead of using that we implemented a heretic IOCTL-based
>> interface for user-space; at least with it you could create a
>> driver (with no interrupt support) for a device in user-space.
>> We actually don't use it, but well, just to get inspiration, here it
>> goes:
>>
>> /**
>>  * \name Window management ioctl numbers
>>  */
>> /** Get a physical window attributes */
>> #define VME_IOCTL_GET_WINDOW_ATTR       _IOWR('V', 0, struct
>> vme_mapping)
>> /** Create a physical window */
>> #define VME_IOCTL_CREATE_WINDOW         _IOW( 'V', 1, struct
>> vme_mapping)
>> /** Destroy a physical window */
>> #define VME_IOCTL_DESTROY_WINDOW        _IOW( 'V', 2, int)
>> /** Create a mapping over a physical window */
>> #define VME_IOCTL_FIND_MAPPING          _IOWR('V', 3, struct
>> vme_mapping)
>> /** Remove a mapping */
>> #define VME_IOCTL_RELEASE_MAPPING       _IOW( 'V', 4, struct
>> vme_mapping)
>> /** Get the create on find failed flag */
>> #define VME_IOCTL_GET_CREATE_ON_FIND_FAIL       _IOR( 'V', 5, unsigned
>> int)
>> /** Set the create on find failed flag */
>> #define VME_IOCTL_SET_CREATE_ON_FIND_FAIL       _IOW( 'V', 6, unsigned
>> int)
>> /** Get the destroy on remove flag */
>> #define VME_IOCTL_GET_DESTROY_ON_REMOVE _IOR( 'V', 7, unsigned int)
>> /** Set the destroy on remove flag */
>> #define VME_IOCTL_SET_DESTROY_ON_REMOVE _IOW( 'V', 8, unsigned int)
>> /** Get bus error status */
>> #define VME_IOCTL_GET_BUS_ERROR         _IOR( 'V', 9, unsigned int)
>> /** Start a DMA transfer */
>> #define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)
>>
>> [ grab the code from here: http://repo.or.cz/w/tsi148vmebridge.git .
>>   Please note there's some backwards-compatible crap for old user-space
>>   LynxOS code, called CES. Ignore it. ]
>>
>>     
I am moving the interface in that direction, I remain unconvinced about 
the contents of your vme_mapping structure, it's too tsi-148 specific.

>> By the way, please try to be consistent with naming, e.g. I see window,
>> then see "image", and I have to think hard to see whether they mean
>> the same or not.. err, do they?
>>     
Yes - I don't know where image came from, probably one of the old VME 
drivers I looked at. I will look at getting rid of that term.

Martyn
>> E.
>>
>> (I'll leave this here because of added Cc)
>> Greg K-H wrote:
>>     
>>> From: Martyn Welch <martyn.welch@gefanuc.com>
>>>
>>> Adds a VME userspace access driver
>>>
>>> Signed-off-by: Martyn Welch <martyn.welch@gefanuc.com>
>>> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
>>>
>>> ---
>>>  drivers/staging/vme/Kconfig            |    4 
>>>  drivers/staging/vme/Makefile           |    2 
>>>  drivers/staging/vme/devices/Kconfig    |    8 
>>>  drivers/staging/vme/devices/Makefile   |    5 
>>>  drivers/staging/vme/devices/vme_user.c |  771 +++++++++++++++++++++++++++++++++
>>>  drivers/staging/vme/devices/vme_user.h |   43 +
>>>  6 files changed, 830 insertions(+), 3 deletions(-)
>>>
>>> --- /dev/null
>>> +++ b/drivers/staging/vme/devices/Kconfig
>>> @@ -0,0 +1,8 @@
>>> +comment "VME Device Drivers"
>>> +
>>> +config VME_USER
>>> +	tristate "VME user space access driver"
>>> +	help
>>> +	  If you say Y here you want to be able to access a limited number of
>>> +	  VME windows in a manner at least semi-compatible with the interface
>>> +	  provided with the original driver at http://vmelinux.org/.
>>> --- /dev/null
>>> +++ b/drivers/staging/vme/devices/Makefile
>>> @@ -0,0 +1,5 @@
>>> +#
>>> +# Makefile for the VME device drivers.
>>> +#
>>> +
>>> +obj-$(CONFIG_VME_USER)		+= vme_user.o
>>> --- /dev/null
>>> +++ b/drivers/staging/vme/devices/vme_user.c
>>> @@ -0,0 +1,771 @@
>>> +/*
>>> + * VMEbus User access driver
>>> + *
>>> + * Author: Martyn Welch <martyn.welch@gefanuc.com>
>>> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>>> + *
>>> + * Based on work by:
>>> + *   Tom Armistead and Ajit Prem
>>> + *     Copyright 2004 Motorola Inc.
>>> + *
>>> + *
>>> + * This program is free software; you can redistribute  it and/or modify it
>>> + * under  the terms of  the GNU General  Public License as published by the
>>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>>> + * option) any later version.
>>> + */
>>> +
>>> +#include <linux/cdev.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/init.h>
>>> +#include <linux/ioctl.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/mm.h>
>>> +#include <linux/module.h>
>>> +#include <linux/pagemap.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/semaphore.h>
>>> +#include <linux/spinlock.h>
>>> +#include <linux/syscalls.h>
>>> +#include <linux/types.h>
>>> +#include <linux/version.h>
>>> +
>>> +#include <asm/io.h>
>>> +#include <asm/uaccess.h>
>>> +
>>> +#include "../vme.h"
>>> +#include "vme_user.h"
>>> +
>>> +/* Currently Documentation/devices.txt defines the following for VME:
>>> + *
>>> + * 221 char	VME bus
>>> + * 		  0 = /dev/bus/vme/m0		First master image
>>> + * 		  1 = /dev/bus/vme/m1		Second master image
>>> + * 		  2 = /dev/bus/vme/m2		Third master image
>>> + * 		  3 = /dev/bus/vme/m3		Fourth master image
>>> + * 		  4 = /dev/bus/vme/s0		First slave image
>>> + * 		  5 = /dev/bus/vme/s1		Second slave image
>>> + * 		  6 = /dev/bus/vme/s2		Third slave image
>>> + * 		  7 = /dev/bus/vme/s3		Fourth slave image
>>> + * 		  8 = /dev/bus/vme/ctl		Control
>>> + *
>>> + * 		It is expected that all VME bus drivers will use the
>>> + * 		same interface.  For interface documentation see
>>> + * 		http://www.vmelinux.org/.
>>> + *
>>> + * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
>>> + * even support the tsi148 chipset (which has 8 master and 8 slave windows).
>>> + * We'll run with this or now as far as possible, however it probably makes
>>> + * sense to get rid of the old mappings and just do everything dynamically.
>>> + *
>>> + * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
>>> + * defined above and try to support at least some of the interface from
>>> + * http://www.vmelinux.org/ as an alternative drive can be written providing a
>>> + * saner interface later.
>>> + */
>>> +#define VME_MAJOR	221	/* VME Major Device Number */
>>> +#define VME_DEVS	9	/* Number of dev entries */
>>> +
>>> +#define MASTER_MINOR	0
>>> +#define MASTER_MAX	3
>>> +#define SLAVE_MINOR	4
>>> +#define SLAVE_MAX	7
>>> +#define CONTROL_MINOR	8
>>> +
>>> +#define PCI_BUF_SIZE  0x20000	/* Size of one slave image buffer */
>>> +
>>> +/*
>>> + * Structure to handle image related parameters.
>>> + */
>>> +typedef struct {
>>> +	void __iomem *kern_buf;	/* Buffer address in kernel space */
>>> +	dma_addr_t pci_buf;	/* Buffer address in PCI address space */
>>> +	unsigned long long size_buf;	/* Buffer size */
>>> +	struct semaphore sem;	/* Semaphore for locking image */
>>> +	struct device *device;	/* Sysfs device */
>>> +	struct vme_resource *resource;	/* VME resource */
>>> +	int users;		/* Number of current users */
>>> +} image_desc_t;
>>> +static image_desc_t image[VME_DEVS];
>>> +
>>> +typedef struct {
>>> +	unsigned long reads;
>>> +	unsigned long writes;
>>> +	unsigned long ioctls;
>>> +	unsigned long irqs;
>>> +	unsigned long berrs;
>>> +	unsigned long dmaErrors;
>>> +	unsigned long timeouts;
>>> +	unsigned long external;
>>> +} driver_stats_t;
>>> +static driver_stats_t statistics;
>>> +
>>> +struct cdev *vme_user_cdev;		/* Character device */
>>> +struct class *vme_user_sysfs_class;	/* Sysfs class */
>>> +struct device *vme_user_bridge;		/* Pointer to the bridge device */
>>> +
>>> +static char driver_name[] = "vme_user";
>>> +
>>> +static const int type[VME_DEVS] = {	MASTER_MINOR,	MASTER_MINOR,
>>> +					MASTER_MINOR,	MASTER_MINOR,
>>> +					SLAVE_MINOR,	SLAVE_MINOR,
>>> +					SLAVE_MINOR,	SLAVE_MINOR,
>>> +					CONTROL_MINOR
>>> +				};
>>> +
>>> +
>>> +static int vme_user_open(struct inode *, struct file *);
>>> +static int vme_user_release(struct inode *, struct file *);
>>> +static ssize_t vme_user_read(struct file *, char *, size_t, loff_t *);
>>> +static ssize_t vme_user_write(struct file *, const char *, size_t, loff_t *);
>>> +static loff_t vme_user_llseek(struct file *, loff_t, int);
>>> +static int vme_user_ioctl(struct inode *, struct file *, unsigned int,
>>> +	unsigned long);
>>> +
>>> +static int __init vme_user_probe(struct device *dev);
>>> +
>>> +static struct file_operations vme_user_fops = {
>>> +        .open = vme_user_open,
>>> +        .release = vme_user_release,
>>> +        .read = vme_user_read,
>>> +        .write = vme_user_write,
>>> +        .llseek = vme_user_llseek,
>>> +        .ioctl = vme_user_ioctl,
>>> +};
>>> +
>>> +
>>> +/*
>>> + * Reset all the statistic counters
>>> + */
>>> +static void reset_counters(void)
>>> +{
>>> +        statistics.reads = 0;
>>> +        statistics.writes = 0;
>>> +        statistics.ioctls = 0;
>>> +        statistics.irqs = 0;
>>> +        statistics.berrs = 0;
>>> +        statistics.dmaErrors = 0;
>>> +        statistics.timeouts = 0;
>>> +}
>>> +
>>> +void lmcall(int monitor)
>>> +{
>>> +	printk("Caught Location Monitor %d access\n", monitor);
>>> +}
>>> +
>>> +static void tests(void)
>>> +{
>>> +	struct vme_resource *dma_res;
>>> +	struct vme_dma_list *dma_list;
>>> +	struct vme_dma_attr *pattern_attr, *vme_attr;
>>> +
>>> +	int retval;
>>> +	unsigned int data;
>>> +
>>> +	printk("Running VME DMA test\n");
>>> +	dma_res = vme_request_dma(vme_user_bridge);
>>> +	dma_list = vme_new_dma_list(dma_res);
>>> +	pattern_attr = vme_dma_pattern_attribute(0x0,
>>> +		VME_DMA_PATTERN_WORD |
>>> +			VME_DMA_PATTERN_INCREMENT);
>>> +	vme_attr = vme_dma_vme_attribute(0x10000, VME_A32,
>>> +		VME_SCT, VME_D32);
>>> +	retval = vme_dma_list_add(dma_list, pattern_attr,
>>> +		vme_attr, 0x10000);
>>> +#if 0
>>> +	vme_dma_free_attribute(vme_attr);
>>> +	vme_attr = vme_dma_vme_attribute(0x20000, VME_A32,
>>> +		VME_SCT, VME_D32);
>>> +	retval = vme_dma_list_add(dma_list, pattern_attr,
>>> +		vme_attr, 0x10000);
>>> +#endif
>>> +	retval = vme_dma_list_exec(dma_list);
>>> +	vme_dma_free_attribute(pattern_attr);
>>> +	vme_dma_free_attribute(vme_attr);
>>> +	vme_dma_list_free(dma_list);
>>> +#if 0
>>> +	printk("Generating a VME interrupt\n");
>>> +	vme_generate_irq(dma_res, 0x3, 0xaa);
>>> +	printk("Interrupt returned\n");
>>> +#endif
>>> +	vme_dma_free(dma_res);
>>> +
>>> +	/* Attempt RMW */
>>> +	data = vme_master_rmw(image[0].resource, 0x80000000, 0x00000000,
>>> +		0x80000000, 0);
>>> +	printk("RMW returned 0x%8.8x\n", data);
>>> +
>>> +
>>> +	/* Location Monitor */
>>> +	printk("vme_lm_set:%d\n", vme_lm_set(vme_user_bridge, 0x60000, VME_A32, VME_SCT | VME_USER | VME_DATA));
>>> +	printk("vme_lm_attach:%d\n", vme_lm_attach(vme_user_bridge, 0, lmcall));
>>> +
>>> +	printk("Board in VME slot:%d\n", vme_slot_get(vme_user_bridge));
>>> +}
>>> +
>>> +static int vme_user_open(struct inode *inode, struct file *file)
>>> +{
>>> +	int err;
>>> +	unsigned int minor = MINOR(inode->i_rdev);
>>> +
>>> +	down(&image[minor].sem);
>>> +	/* Only allow device to be opened if a resource is allocated */
>>> +	if (image[minor].resource == NULL) {
>>> +		printk(KERN_ERR "No resources allocated for device\n");
>>> +		err = -EINVAL;
>>> +		goto err_res;
>>> +	}
>>> +
>>> +	/* Increment user count */
>>> +	image[minor].users++;
>>> +
>>> +	up(&image[minor].sem);
>>> +
>>> +	return 0;
>>> +
>>> +err_res:
>>> +	up(&image[minor].sem);
>>> +
>>> +	return err;
>>> +}
>>> +
>>> +static int vme_user_release(struct inode *inode, struct file *file)
>>> +{
>>> +	unsigned int minor = MINOR(inode->i_rdev);
>>> +
>>> +	down(&image[minor].sem);
>>> +
>>> +	/* Decrement user count */
>>> +	image[minor].users--;
>>> +
>>> +	up(&image[minor].sem);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * We are going ot alloc a page during init per window for small transfers.
>>> + * Small transfers will go VME -> buffer -> user space. Larger (more than a
>>> + * page) transfers will lock the user space buffer into memory and then
>>> + * transfer the data directly into the user space buffers.
>>> + */
>>> +static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
>>> +	loff_t *ppos)
>>> +{
>>> +	ssize_t retval;
>>> +	ssize_t copied = 0;
>>> +
>>> +	if (count <= image[minor].size_buf) {
>>> +		/* We copy to kernel buffer */
>>> +		copied = vme_master_read(image[minor].resource,
>>> +			image[minor].kern_buf, count, *ppos);
>>> +		if (copied < 0) {
>>> +			return (int)copied;
>>> +		}
>>> +
>>> +		retval = __copy_to_user(buf, image[minor].kern_buf,
>>> +			(unsigned long)copied);
>>> +		if (retval != 0) {
>>> +			copied = (copied - retval);
>>> +			printk("User copy failed\n");
>>> +			return -EINVAL;
>>> +		}
>>> +
>>> +	} else {
>>> +		/* XXX Need to write this */
>>> +		printk("Currently don't support large transfers\n");
>>> +		/* Map in pages from userspace */
>>> +
>>> +		/* Call vme_master_read to do the transfer */
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return copied;
>>> +}
>>> +
>>> +/*
>>> + * We are going ot alloc a page during init per window for small transfers.
>>> + * Small transfers will go user space -> buffer -> VME. Larger (more than a
>>> + * page) transfers will lock the user space buffer into memory and then
>>> + * transfer the data directly from the user space buffers out to VME.
>>> + */
>>> +static ssize_t resource_from_user(unsigned int minor, const char *buf,
>>> +	size_t count, loff_t *ppos)
>>> +{
>>> +	ssize_t retval;
>>> +	ssize_t copied = 0;
>>> +
>>> +	if (count <= image[minor].size_buf) {
>>> +		retval = __copy_from_user(image[minor].kern_buf, buf,
>>> +			(unsigned long)count);
>>> +		if (retval != 0)
>>> +			copied = (copied - retval);
>>> +		else
>>> +			copied = count;
>>> +
>>> +		copied = vme_master_write(image[minor].resource,
>>> +			image[minor].kern_buf, copied, *ppos);
>>> +	} else {
>>> +		/* XXX Need to write this */
>>> +		printk("Currently don't support large transfers\n");
>>> +		/* Map in pages from userspace */
>>> +
>>> +		/* Call vme_master_write to do the transfer */
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return copied;
>>> +}
>>> +
>>> +static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
>>> +	size_t count, loff_t *ppos)
>>> +{
>>> +	void __iomem *image_ptr;
>>> +	ssize_t retval;
>>> +
>>> +	image_ptr = image[minor].kern_buf + *ppos;
>>> +
>>> +	retval = __copy_to_user(buf, image_ptr, (unsigned long)count);
>>> +	if (retval != 0) {
>>> +		retval = (count - retval);
>>> +		printk(KERN_WARNING "Partial copy to userspace\n");
>>> +	} else
>>> +		retval = count;
>>> +
>>> +	/* Return number of bytes successfully read */
>>> +	return retval;
>>> +}
>>> +
>>> +static ssize_t buffer_from_user(unsigned int minor, const char *buf,
>>> +	size_t count, loff_t *ppos)
>>> +{
>>> +	void __iomem *image_ptr;
>>> +	size_t retval;
>>> +
>>> +	image_ptr = image[minor].kern_buf + *ppos;
>>> +
>>> +	retval = __copy_from_user(image_ptr, buf, (unsigned long)count);
>>> +	if (retval != 0) {
>>> +		retval = (count - retval);
>>> +		printk(KERN_WARNING "Partial copy to userspace\n");
>>> +	} else
>>> +		retval = count;
>>> +
>>> +	/* Return number of bytes successfully read */
>>> +	return retval;
>>> +}
>>> +
>>> +static ssize_t vme_user_read(struct file *file, char *buf, size_t count,
>>> +			loff_t * ppos)
>>> +{
>>> +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
>>> +	ssize_t retval;
>>> +	size_t image_size;
>>> +	size_t okcount;
>>> +
>>> +	down(&image[minor].sem);
>>> +
>>> +	/* XXX Do we *really* want this helper - we can use vme_*_get ? */
>>> +	image_size = vme_get_size(image[minor].resource);
>>> +
>>> +	/* Ensure we are starting at a valid location */
>>> +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
>>> +		up(&image[minor].sem);
>>> +		return 0;
>>> +	}
>>> +
>>> +	/* Ensure not reading past end of the image */
>>> +	if (*ppos + count > image_size)
>>> +		okcount = image_size - *ppos;
>>> +	else
>>> +		okcount = count;
>>> +
>>> +	switch (type[minor]){
>>> +	case MASTER_MINOR:
>>> +		retval = resource_to_user(minor, buf, okcount, ppos);
>>> +		break;
>>> +	case SLAVE_MINOR:
>>> +		retval = buffer_to_user(minor, buf, okcount, ppos);
>>> +		break;
>>> +	default:
>>> +		retval = -EINVAL;
>>> +	}
>>> +
>>> +	up(&image[minor].sem);
>>> +
>>> +	if (retval > 0)
>>> +		*ppos += retval;
>>> +
>>> +	return retval;
>>> +}
>>> +
>>> +static ssize_t vme_user_write(struct file *file, const char *buf, size_t count,
>>> +			 loff_t *ppos)
>>> +{
>>> +	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
>>> +	ssize_t retval;
>>> +	size_t image_size;
>>> +	size_t okcount;
>>> +
>>> +	down(&image[minor].sem);
>>> +
>>> +	image_size = vme_get_size(image[minor].resource);
>>> +
>>> +	/* Ensure we are starting at a valid location */
>>> +	if ((*ppos < 0) || (*ppos > (image_size - 1))) {
>>> +		up(&image[minor].sem);
>>> +		return 0;
>>> +	}
>>> +
>>> +	/* Ensure not reading past end of the image */
>>> +	if (*ppos + count > image_size)
>>> +		okcount = image_size - *ppos;
>>> +	else
>>> +		okcount = count;
>>> +
>>> +	switch (type[minor]){
>>> +	case MASTER_MINOR:
>>> +		retval = resource_from_user(minor, buf, okcount, ppos);
>>> +		break;
>>> +	case SLAVE_MINOR:
>>> +		retval = buffer_from_user(minor, buf, okcount, ppos);
>>> +		break;
>>> +	default:
>>> +		retval = -EINVAL;
>>> +	}
>>> +
>>> +	up(&image[minor].sem);
>>> +
>>> +	if (retval > 0)
>>> +		*ppos += retval;
>>> +
>>> +	return retval;
>>> +}
>>> +
>>> +static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
>>> +{
>>> +	printk(KERN_ERR "Llseek currently incomplete\n");
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static int vme_user_ioctl(struct inode *inode, struct file *file,
>>> +	unsigned int cmd, unsigned long arg)
>>> +{
>>> +	unsigned int minor = MINOR(inode->i_rdev);
>>> +#if 0
>>> +	int ret_val;
>>> +#endif
>>> +	unsigned long copyRet;
>>> +	vme_slave_t slave;
>>> +
>>> +	statistics.ioctls++;
>>> +	switch (type[minor]) {
>>> +	case CONTROL_MINOR:
>>> +		break;
>>> +	case MASTER_MINOR:
>>> +		break;
>>> +	case SLAVE_MINOR:
>>> +		switch (cmd) {
>>> +		case VME_SET_SLAVE:
>>> +
>>> +			copyRet = copy_from_user(&slave, (char *)arg,
>>> +				sizeof(slave));
>>> +			if (copyRet != 0) {
>>> +				printk(KERN_WARNING "Partial copy from "
>>> +					"userspace\n");
>>> +				return -EFAULT;
>>> +			}
>>> +
>>> +			return vme_slave_set(image[minor].resource,
>>> +				slave.enable, slave.vme_addr, slave.size,
>>> +				image[minor].pci_buf, slave.aspace,
>>> +				slave.cycle);
>>> +
>>> +			break;
>>> +#if 0
>>> +		case VME_GET_SLAVE:
>>> +			vme_slave_t slave;
>>> +
>>> +			ret_val = vme_slave_get(minor, &iRegs);
>>> +
>>> +			copyRet = copy_to_user((char *)arg, &slave,
>>> +				sizeof(slave));
>>> +			if (copyRet != 0) {
>>> +				printk(KERN_WARNING "Partial copy to "
>>> +					"userspace\n");
>>> +				return -EFAULT;
>>> +			}
>>> +
>>> +			return ret_val;
>>> +			break;
>>> +#endif
>>> +		}
>>> +		break;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +
>>> +/*
>>> + * Unallocate a previously allocated buffer
>>> + */
>>> +static void buf_unalloc (int num)
>>> +{
>>> +	if (image[num].kern_buf) {
>>> +#ifdef VME_DEBUG
>>> +		printk(KERN_DEBUG "UniverseII:Releasing buffer at %p\n",
>>> +			image[num].pci_buf);
>>> +#endif
>>> +
>>> +		vme_free_consistent(image[num].resource, image[num].size_buf,
>>> +			image[num].kern_buf, image[num].pci_buf);
>>> +
>>> +		image[num].kern_buf = NULL;
>>> +		image[num].pci_buf = 0;
>>> +		image[num].size_buf = 0;
>>> +
>>> +#ifdef VME_DEBUG
>>> +	} else {
>>> +		printk(KERN_DEBUG "UniverseII: Buffer not allocated\n");
>>> +#endif
>>> +	}
>>> +}
>>> +
>>> +static struct vme_driver vme_user_driver = {
>>> +        .name = driver_name,
>>> +        .probe = vme_user_probe,
>>> +};
>>> +
>>> +
>>> +/*
>>> + * In this simple access driver, the old behaviour is being preserved as much
>>> + * as practical. We will therefore reserve the buffers and request the images
>>> + * here so that we don't have to do it later.
>>> + */
>>> +static int __init vme_bridge_init(void)
>>> +{
>>> +	int retval;
>>> +	printk(KERN_INFO "VME User Space Access Driver\n");
>>> +	printk("vme_user_driver:%p\n", &vme_user_driver);
>>> +	retval = vme_register_driver(&vme_user_driver);
>>> +	printk("vme_register_driver returned %d\n", retval);
>>> +	return retval;
>>> +}
>>> +
>>> +/*
>>> + * This structure gets passed a device, this should be the device created at
>>> + * registration.
>>> + */
>>> +static int __init vme_user_probe(struct device *dev)
>>> +{
>>> +	int i, err;
>>> +	char name[8];
>>> +
>>> +	printk("Running vme_user_probe()\n");
>>> +
>>> +	/* Pointer to the bridge device */
>>> +	vme_user_bridge = dev;
>>> +
>>> +	/* Initialise descriptors */
>>> +	for (i = 0; i < VME_DEVS; i++) {
>>> +		image[i].kern_buf = NULL;
>>> +		image[i].pci_buf = 0;
>>> +		init_MUTEX(&(image[i].sem));
>>> +		image[i].device = NULL;
>>> +		image[i].resource = NULL;
>>> +		image[i].users = 0;
>>> +	}
>>> +
>>> +	/* Initialise statistics counters */
>>> +	reset_counters();
>>> +
>>> +	/* Assign major and minor numbers for the driver */
>>> +	err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS,
>>> +		driver_name);
>>> +	if (err) {
>>> +		printk(KERN_WARNING "%s: Error getting Major Number %d for "
>>> +		"driver.\n", driver_name, VME_MAJOR);
>>> +		goto err_region;
>>> +	}
>>> +
>>> +	/* Register the driver as a char device */
>>> +	vme_user_cdev = cdev_alloc();
>>> +	vme_user_cdev->ops = &vme_user_fops;
>>> +	vme_user_cdev->owner = THIS_MODULE;
>>> +	err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
>>> +	if (err) {
>>> +		printk(KERN_WARNING "%s: cdev_all failed\n", driver_name);
>>> +		goto err_char;
>>> +	}
>>> +
>>> +	/* Request slave resources and allocate buffers (128kB wide) */
>>> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
>>> +		/* XXX Need to properly request attributes */
>>> +		image[i].resource = vme_slave_request(vme_user_bridge,
>>> +			VME_A16, VME_SCT);
>>> +		if (image[i].resource == NULL) {
>>> +			printk(KERN_WARNING "Unable to allocate slave "
>>> +				"resource\n");
>>> +			goto err_buf;
>>> +		}
>>> +		image[i].size_buf = PCI_BUF_SIZE;
>>> +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
>>> +			image[i].size_buf, &(image[i].pci_buf));
>>> +		if (image[i].kern_buf == NULL) {
>>> +			printk(KERN_WARNING "Unable to allocate memory for "
>>> +				"buffer\n");
>>> +			image[i].pci_buf = 0;
>>> +			vme_slave_free(image[i].resource);
>>> +			err = -ENOMEM;
>>> +			goto err_buf;
>>> +		}
>>> +	}
>>> +
>>> +	/*
>>> +	 * Request master resources allocate page sized buffers for small
>>> +	 * reads and writes
>>> +	 */
>>> +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
>>> +		/* XXX Need to properly request attributes */
>>> +		image[i].resource = vme_master_request(vme_user_bridge,
>>> +			VME_A32, VME_SCT, VME_D32);
>>> +		if (image[i].resource == NULL) {
>>> +			printk(KERN_WARNING "Unable to allocate master "
>>> +				"resource\n");
>>> +			goto err_buf;
>>> +		}
>>> +		image[i].size_buf = PAGE_SIZE;
>>> +		image[i].kern_buf = vme_alloc_consistent(image[i].resource,
>>> +			image[i].size_buf, &(image[i].pci_buf));
>>> +		if (image[i].kern_buf == NULL) {
>>> +			printk(KERN_WARNING "Unable to allocate memory for "
>>> +				"buffer\n");
>>> +			image[i].pci_buf = 0;
>>> +			vme_master_free(image[i].resource);
>>> +			err = -ENOMEM;
>>> +			goto err_buf;
>>> +		}
>>> +	}
>>> +
>>> +	/* Setup some debug windows */
>>> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
>>> +		err = vme_slave_set(image[i].resource, 1, 0x4000*(i-4),
>>> +			0x4000, image[i].pci_buf, VME_A16,
>>> +			VME_SCT | VME_SUPER | VME_USER | VME_PROG | VME_DATA);
>>> +		if (err != 0) {
>>> +			printk(KERN_WARNING "Failed to configure window\n");
>>> +			goto err_buf;
>>> +		}
>>> +	}
>>> +	for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
>>> +		err = vme_master_set(image[i].resource, 1,
>>> +			(0x10000 + (0x10000*i)), 0x10000,
>>> +			VME_A32, VME_SCT | VME_USER | VME_DATA, VME_D32);
>>> +		if (err != 0) {
>>> +			printk(KERN_WARNING "Failed to configure window\n");
>>> +			goto err_buf;
>>> +		}
>>> +	}
>>> +
>>> +	/* Create sysfs entries - on udev systems this creates the dev files */
>>> +	vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
>>> +	if (IS_ERR(vme_user_sysfs_class)) {
>>> +		printk(KERN_ERR "Error creating vme_user class.\n");
>>> +		err = PTR_ERR(vme_user_sysfs_class);
>>> +		goto err_class;
>>> +	}
>>> +
>>> +	/* Add sysfs Entries */
>>> +	for (i=0; i<VME_DEVS; i++) {
>>> +		switch (type[i]) {
>>> +		case MASTER_MINOR:
>>> +			sprintf(name,"bus/vme/m%%d");
>>> +			break;
>>> +		case CONTROL_MINOR:
>>> +			sprintf(name,"bus/vme/ctl");
>>> +			break;
>>> +		case SLAVE_MINOR:
>>> +			sprintf(name,"bus/vme/s%%d");
>>> +			break;
>>> +		default:
>>> +			err = -EINVAL;
>>> +			goto err_sysfs;
>>> +			break;
>>> +		}
>>> +
>>> +		image[i].device =
>>> +			device_create(vme_user_sysfs_class, NULL,
>>> +				MKDEV(VME_MAJOR, i), NULL, name,
>>> +				(type[i] == SLAVE_MINOR)? i - (MASTER_MAX + 1) : i);
>>> +		if (IS_ERR(image[i].device)) {
>>> +			printk("%s: Error creating sysfs device\n",
>>> +				driver_name);
>>> +			err = PTR_ERR(image[i].device);
>>> +			goto err_sysfs;
>>> +		}
>>> +	}
>>> +
>>> +	/* XXX Run tests */
>>> +	/*
>>> +	tests();
>>> +	*/
>>> +
>>> +	return 0;
>>> +
>>> +	/* Ensure counter set correcty to destroy all sysfs devices */
>>> +	i = VME_DEVS;
>>> +err_sysfs:
>>> +	while (i > 0){
>>> +		i--;
>>> +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
>>> +	}
>>> +	class_destroy(vme_user_sysfs_class);
>>> +
>>> +	/* Ensure counter set correcty to unalloc all slave buffers */
>>> +	i = SLAVE_MAX + 1;
>>> +err_buf:
>>> +	while (i > SLAVE_MINOR){
>>> +		i--;
>>> +		vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
>>> +		vme_slave_free(image[i].resource);
>>> +		buf_unalloc(i);
>>> +	}
>>> +err_class:
>>> +	cdev_del(vme_user_cdev);
>>> +err_char:
>>> +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
>>> +err_region:
>>> +	return err;
>>> +}
>>> +
>>> +static void __exit vme_bridge_exit(void)
>>> +{
>>> +	int i;
>>> +
>>> +	/* Remove sysfs Entries */
>>> +	for(i=0; i<VME_DEVS; i++) {
>>> +		device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
>>> +	}
>>> +	class_destroy(vme_user_sysfs_class);
>>> +
>>> +	for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
>>> +		buf_unalloc(i);
>>> +	}
>>> +
>>> +	/* Unregister device driver */
>>> +	cdev_del(vme_user_cdev);
>>> +
>>> +	/* Unregiser the major and minor device numbers */
>>> +	unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
>>> +}
>>> +
>>> +MODULE_DESCRIPTION("VME User Space Access Driver");
>>> +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
>>> +MODULE_LICENSE("GPL");
>>> +
>>> +module_init(vme_bridge_init);
>>> +module_exit(vme_bridge_exit);
>>> --- /dev/null
>>> +++ b/drivers/staging/vme/devices/vme_user.h
>>> @@ -0,0 +1,43 @@
>>> +
>>> +/*
>>> + * VMEbus Master Window Configuration Structure
>>> + */
>>> +typedef struct {
>>> +	char enable;			/* State of Window */
>>> +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
>>> +	unsigned long long size;	/* Window Size */
>>> +	vme_address_t aspace;		/* Address Space */
>>> +	vme_cycle_t cycle;		/* Cycle properties */
>>> +	vme_width_t dwidth;		/* Maximum Data Width */
>>> +#if 0
>>> +	char prefetchEnable;		/* Prefetch Read Enable State */
>>> +	int prefetchSize;		/* Prefetch Read Size (Cache Lines) */
>>> +	char wrPostEnable;		/* Write Post State */
>>> +#endif
>>> +} vme_master_t;
>>> +
>>> +
>>> +/*
>>> + * IOCTL Commands and structures
>>> + */
>>> +
>>> +/* Magic number for use in ioctls */
>>> +#define VME_IOC_MAGIC 0xAE
>>> +
>>> +
>>> +/* VMEbus Slave Window Configuration Structure */
>>> +typedef struct {
>>> +	char enable;			/* State of Window */
>>> +	unsigned long long vme_addr;	/* Starting Address on the VMEbus */
>>> +	unsigned long long size;	/* Window Size */
>>> +	vme_address_t aspace;		/* Address Space */
>>> +	vme_cycle_t cycle;		/* Cycle properties */
>>> +#if 0
>>> +	char wrPostEnable;		/* Write Post State */
>>> +	char rmwLock;			/* Lock PCI during RMW Cycles */
>>> +	char data64BitCapable;		/* non-VMEbus capable of 64-bit Data */
>>> +#endif
>>> +} vme_slave_t;
>>> +
>>> +#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 10, vme_slave_t)
>>> +
>>> --- a/drivers/staging/vme/Kconfig
>>> +++ b/drivers/staging/vme/Kconfig
>>> @@ -11,7 +11,7 @@ menuconfig VME
>>>  if VME
>>>  
>>>  #source "drivers/staging/vme/bridges/Kconfig"
>>> -#
>>> -#source "drivers/staging/vme/devices/Kconfig"
>>> +
>>> +source "drivers/staging/vme/devices/Kconfig"
>>>  
>>>  endif # VME
>>> --- a/drivers/staging/vme/Makefile
>>> +++ b/drivers/staging/vme/Makefile
>>> @@ -4,4 +4,4 @@
>>>  obj-$(CONFIG_VME)		+= vme.o
>>>  
>>>  #obj-y				+= bridges/
>>> -#obj-y				+= devices/
>>> +obj-y				+= devices/
>>>
>>>
>>>       


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 12:44       ` Martyn Welch
@ 2009-08-10 14:14         ` Emilio G. Cota
  2009-08-10 15:31           ` Emilio G. Cota
  2009-08-10 15:53           ` Martyn Welch
  2009-08-11  4:54         ` Mike Frysinger
  1 sibling, 2 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 14:14 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Emilio G. Cota wrote:
>> - semaphores? isn't it screaming for mutexes?
>
> The semaphores are initialized in mutex mode.

I know, but then please use mutexes.

>> - some function signatures (get_whatever) pass too many
>>   pointers; it's rather ugly. Passing a pointer to an
>>   appropriate struct seems a better solution.
>>   
>
> When implementing these functions it seemed like a simpler option to  
> pass the parameters rather than defining yet another structure and using  
> it for a single call. If the *_get functions are changed then the *_set  
> functions should also be changed for consistency. I'm not overly  
> bothered either way - if the consensus is that a structure would be  
> better then we can go with a structure.

Yeh, In my opinion a structure there would be prettier. However
let's put this off a bit, I'm currently re-working this a bit.

>> - vme_alloc_consistent looks pretty fishy; why don't you pass
>>   that responsibility to the specific master? There you obviously
>>   know if you're bridging over PCI or whatever. Or even better;
>>   why is this needed at all?
>>   
>
> In the model I have chosen it is up to the VME device driver to create  
> buffers rather than the VME bridge drivers. After all it is the device  
> drivers for the specific hardware found on the VME bus that knows what  
> it is going to do with these buffers. This method is provided as a  
> helper. It ensures that a VME device driver (not to be confused with the  
> VME bridge driver) is able to allocate a buffer suitable for utilizing  
> the DMA engines found in the bridge or indeed to be mapped onto VME  
> though a slave window without explicitly knowing which VME bridge is  
> being used.

hmm I see. The way you provide coherent DMA mappings for VME is through
providing coherent (consistent) mappings for PCI, and then mapping the
VME space onto those. I haven't tested if this works, but sounds
reasonable.

> Master windows don't require a contiguous buffer, are you referring to  
> something else when you say master?
I was thinking of the hypothetical case where you'd have a bridge not
over PCI; but anyway since quite a few things seem tied to PCI this
is fine.

>> - Please explain me what all the DMA functions do; are they
>>   meant to be used by master or slaves?
>>   
> The TODO file (drivers/staging/vme/TODO) contains a description of the  
> current API including the DMA functions, does that provide enough of a  
> description?

I've had a closer look; it seems to me that most of it is unnecessary;
there's no show those lists to a driver. I'd just provide a single
'do_dma(attributes)' call that sleeps until it's done (or similar).

>> Have a look at the interface for slaves we've got for our tsi148
>> driver, available at:
>> http://repo.or.cz/w/tsi148vmebridge.git
>>
>> /* API for new drivers */
>> extern int vme_request_irq(unsigned int, int (*)(void *),
>>                            void *, const char *);
[snip]
>> extern int vme_bus_error_check(int);
>>
>> That's pretty thin and it covers our slaves' needs. Do you see
>> anything missing there?
>>   
>
> This interface, especially struct vme_mapping seems to have been written  
> solely for the tsi-148 bridge. The API I have defined aims to provide a  
> consistent API across multiple VME bridges. Whilst this API clearly  
> works for you with the tsi-148 I am unsure how suitable it will be for  
> other bridge chips.
>
> The API I have proposed is designed to support more than just the tsi148  
> chipset. Have you thought about how the above API will be supported on  
> other VME bridges?

We only have access to tsi148 chips. Since apparently there aren't
many bridges around, I think is saner to just provide an interface
that works well with the devices we have access to (i.e. tsi148 and
Tundra Universe in your case), and when new chips come along, we simply
modify the interface as needed. Of course this doesn't mean we shouldn't
try to make it generic, but within reason.

>> For masters there's no interface there because it was the
>> master's driver who directly provided these calls to slaves.
>> I had it in my to-do list to split that from the tsi148, in
>> the same fashion as you've done with this work.
>>
>> - Note above the interrupt handler; simply needs the cookie. Also,
>>   shouldn't your vme_request_irq() just require the IRQ vector?
>>   
>
> No. I believe that you are using the term IRQ vector where we would use  
> the term status ID, the value which is returned from an IACK cycle. Your  
> interrupt handling code assigns a single interrupt handler to all  
> interrupt levels, purely using the interrupt vector/status ID to  
> determine which interrupt handler will be used. This adds an artificial  
> limitation and would not work in some instances that we have seen. Our  
> framework provides the ability to attach an interrupt handler to each  
> combination of IRQ level and Status ID/vector.
Fair enough. For sanity we tend not to share IRQ vectors among
different modules, but yes, the interface should know about this.

>> - I'd like to see the whole picture, or 'vertical slice', i.e.
>>   the bus interface + a master + a slave driver. How would
>>   the slave's driver get the addresses and sizes of the mappings,
>>   interrupt lines, etc. for each of the devices it controls?
>>   For the time being we quickly hacked an xml-based scheme to get
>>   this info upon installation, but it's clearly not suitable
>>   for mainline.
>>   
> I have written a test driver for a very old slave card we had lying  
> around. In that case we used module parameters to determine the location  
> of the device on the bus (it used a fixed VME address space). In this  
> instance the interrupt level and ID could be configured in the registers  
> available in the VME address space, hence I added module parameters to  
> allow these to be configured. In this respect configuration of VME  
> devices is very similar to ISA devices - neither of the buses has  
> supported discovery mechanism from the outset and thus old cards. I  
> therefore implemented a mechanism similar to how I believe ISA  
> approaches this.
>
> The framework that I have proposed aims to provide a consistent API to  
> manage allowing the resources provided by the VME bridges to be managed  
> in as a consistent a manner as possible. I believe it is up to the  
> device drivers for the devices found on the VME bus to determine the  
> best way to configure this as it is not provided by the VME 
> specifications.

The problem is how to manage several (say 10) devices that have to be
controlled with the same driver; passing parameters upon loading the
driver's module doesn't seem to scale beyond one device..

At the moment I see no clean way of doing this; right now we're doing
it through an IOCTL, copying from user-space a tree that describes the
devices to install, namely mappings and IRQs for each of them.

Greg, any ideas on how to deal with this?

> For using the VME bus for communication between two SBCs, I think we  
> could use the CRCSR space to provide some information about the  
> resources used on each board for say, a virtual network driver like the  
> rapidIO subsystem provides. Possibly using the "device tree" stuff used  
> on PowerPC, Blackfin and Sparc archs (I believe) for passing device  
> layout between the firmware and kernel at boot.

hmm I don't see how we could implement this common consistent view of
the bus in a way that could work with every bridge. For the time
being I'd defer this and simply pass that responsibility to user-space.

> Sure, np,
>
> Martyn

Thanks,
E.

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

* [PATCH] Staging: vme: fix {master,slave}_get check bug
  2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
  2009-08-08 23:01     ` Emilio G. Cota
@ 2009-08-10 14:52     ` Emilio G. Cota
  2009-08-10 16:50       ` Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 14:52 UTC (permalink / raw)
  To: Greg K-H; +Cc: linux-kernel, devel, Martyn Welch

It's checking the wrong functions; fix it.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 drivers/staging/vme/vme.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/vme/vme.c b/drivers/staging/vme/vme.c
index 8ee1192..ed2eeb0 100644
--- a/drivers/staging/vme/vme.c
+++ b/drivers/staging/vme/vme.c
@@ -338,7 +338,7 @@ int vme_slave_get (struct vme_resource *resource, int *enabled,
 
 	image = list_entry(resource->entry, struct vme_slave_resource, list);
 
-	if (bridge->slave_set == NULL) {
+	if (bridge->slave_get == NULL) {
 		printk("vme_slave_get not supported\n");
 		return -EINVAL;
 	}
@@ -499,7 +499,7 @@ int vme_master_get (struct vme_resource *resource, int *enabled,
 
 	image = list_entry(resource->entry, struct vme_master_resource, list);
 
-	if (bridge->master_set == NULL) {
+	if (bridge->master_get == NULL) {
 		printk("vme_master_set not supported\n");
 		return -EINVAL;
 	}
-- 
1.6.3.3


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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 13:13         ` Martyn Welch
@ 2009-08-10 15:26           ` Emilio G. Cota
  2009-08-10 16:29             ` Greg KH
  2009-08-10 16:30             ` Martyn Welch
  0 siblings, 2 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 15:26 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
>>> Instead of using that we implemented a heretic IOCTL-based
>>> interface for user-space; at least with it you could create a
[ snip ]
>>> #define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)
>>>
> I am moving the interface in that direction, I remain unconvinced about  
> the contents of your vme_mapping structure, it's too tsi-148 specific.

Could you please point out why is too tsi148-specific?

The point here is that the driver should know *nothing* about
windows, etc. What it should just know is:
- I want a mapping of a certain size to VME address X
The struct provides exactly this.

Also, could you please send me (off-list) documentation of the
Universe bridge? It'd be useful for the work on the generic layer.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 14:14         ` Emilio G. Cota
@ 2009-08-10 15:31           ` Emilio G. Cota
  2009-08-10 16:40             ` Martyn Welch
  2009-08-10 15:53           ` Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 15:31 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> I've had a closer look; it seems to me that most of it is unnecessary;
> there's no show those lists to a driver. I'd just provide a single
> 'do_dma(attributes)' call that sleeps until it's done (or similar).

s/there's no show/there's no point in showing/

Basically the driver simply shouldn't care about these DMA
lists. It just wants to get the work done, no matter how.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 14:14         ` Emilio G. Cota
  2009-08-10 15:31           ` Emilio G. Cota
@ 2009-08-10 15:53           ` Martyn Welch
  2009-08-10 16:26             ` Shawn Bohrer
  2009-08-10 19:38             ` Emilio G. Cota
  1 sibling, 2 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 15:53 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Emilio G. Cota wrote:
>>     
>>> - semaphores? isn't it screaming for mutexes?
>>>       
>> The semaphores are initialized in mutex mode.
>>     
>
> I know, but then please use mutexes.
>
>   
I'm clearly missing something here - can you provide me with an example?
>>> - some function signatures (get_whatever) pass too many
>>>   pointers; it's rather ugly. Passing a pointer to an
>>>   appropriate struct seems a better solution.
>>>   
>>>       
>> When implementing these functions it seemed like a simpler option to  
>> pass the parameters rather than defining yet another structure and using  
>> it for a single call. If the *_get functions are changed then the *_set  
>> functions should also be changed for consistency. I'm not overly  
>> bothered either way - if the consensus is that a structure would be  
>> better then we can go with a structure.
>>     
>
> Yeh, In my opinion a structure there would be prettier. However
> let's put this off a bit, I'm currently re-working this a bit.
>
>   
>>> - vme_alloc_consistent looks pretty fishy; why don't you pass
>>>   that responsibility to the specific master? There you obviously
>>>   know if you're bridging over PCI or whatever. Or even better;
>>>   why is this needed at all?
>>>   
>>>       
>> In the model I have chosen it is up to the VME device driver to create  
>> buffers rather than the VME bridge drivers. After all it is the device  
>> drivers for the specific hardware found on the VME bus that knows what  
>> it is going to do with these buffers. This method is provided as a  
>> helper. It ensures that a VME device driver (not to be confused with the  
>> VME bridge driver) is able to allocate a buffer suitable for utilizing  
>> the DMA engines found in the bridge or indeed to be mapped onto VME  
>> though a slave window without explicitly knowing which VME bridge is  
>> being used.
>>     
>
> hmm I see. The way you provide coherent DMA mappings for VME is through
> providing coherent (consistent) mappings for PCI, and then mapping the
> VME space onto those. I haven't tested if this works, but sounds
> reasonable.
>   
>> Master windows don't require a contiguous buffer, are you referring to  
>> something else when you say master?
>>     
> I was thinking of the hypothetical case where you'd have a bridge not
> over PCI; but anyway since quite a few things seem tied to PCI this
> is fine.
>   

That's one reason for the helper function. If VME bridges are added 
which sit of on other buses then vme_alloc_consistent can be extended to 
support this without requiring VME device drivers to be altered to 
reflect the fact that pci_alloc_consistent might not work for all VME 
bridges.

>>> - Please explain me what all the DMA functions do; are they
>>>   meant to be used by master or slaves?
>>>   
>>>       
>> The TODO file (drivers/staging/vme/TODO) contains a description of the  
>> current API including the DMA functions, does that provide enough of a  
>> description?
>>     
>
> I've had a closer look; it seems to me that most of it is unnecessary;
> there's no show those lists to a driver. I'd just provide a single
> 'do_dma(attributes)' call that sleeps until it's done (or similar).
>
>   
The DMA controller in the tsi-148 can do PCI to PCI; PCI to VME; VME to 
PCI; VME to VME; Patterns to  VME and Patterns to PCI transfers. How do 
you specify all those options without providing a structure where over 
50% of the fields aren't used for any given transfer?

Every bridge I have seen is capable of link-list execution. The API 
provides the ability to do scatter-gather style DMA transfers, this 
could be used as part of a "zero-copy" type driver for high speed VME 
capture devices. How would you support the link-list execution with a 
single call?

I was also looking at the ability to queue DMA transfers if the 
controller was currently busy - if the consensus is that this is 
overkill I will happily remove this.

>>> Have a look at the interface for slaves we've got for our tsi148
>>> driver, available at:
>>> http://repo.or.cz/w/tsi148vmebridge.git
>>>
>>> /* API for new drivers */
>>> extern int vme_request_irq(unsigned int, int (*)(void *),
>>>                            void *, const char *);
>>>       
> [snip]
>   
>>> extern int vme_bus_error_check(int);
>>>
>>> That's pretty thin and it covers our slaves' needs. Do you see
>>> anything missing there?
>>>   
>>>       
>> This interface, especially struct vme_mapping seems to have been written  
>> solely for the tsi-148 bridge. The API I have defined aims to provide a  
>> consistent API across multiple VME bridges. Whilst this API clearly  
>> works for you with the tsi-148 I am unsure how suitable it will be for  
>> other bridge chips.
>>
>> The API I have proposed is designed to support more than just the tsi148  
>> chipset. Have you thought about how the above API will be supported on  
>> other VME bridges?
>>     
>
> We only have access to tsi148 chips. Since apparently there aren't
> many bridges around, I think is saner to just provide an interface
> that works well with the devices we have access to (i.e. tsi148 and
> Tundra Universe in your case), and when new chips come along, we simply
> modify the interface as needed. Of course this doesn't mean we shouldn't
> try to make it generic, but within reason.
>
>   
Agreed - which I believe is the approach I've taken.
>>> For masters there's no interface there because it was the
>>> master's driver who directly provided these calls to slaves.
>>> I had it in my to-do list to split that from the tsi148, in
>>> the same fashion as you've done with this work.
>>>
>>> - Note above the interrupt handler; simply needs the cookie. Also,
>>>   shouldn't your vme_request_irq() just require the IRQ vector?
>>>   
>>>       
>> No. I believe that you are using the term IRQ vector where we would use  
>> the term status ID, the value which is returned from an IACK cycle. Your  
>> interrupt handling code assigns a single interrupt handler to all  
>> interrupt levels, purely using the interrupt vector/status ID to  
>> determine which interrupt handler will be used. This adds an artificial  
>> limitation and would not work in some instances that we have seen. Our  
>> framework provides the ability to attach an interrupt handler to each  
>> combination of IRQ level and Status ID/vector.
>>     
> Fair enough. For sanity we tend not to share IRQ vectors among
> different modules, but yes, the interface should know about this.
>
>   
>>> - I'd like to see the whole picture, or 'vertical slice', i.e.
>>>   the bus interface + a master + a slave driver. How would
>>>   the slave's driver get the addresses and sizes of the mappings,
>>>   interrupt lines, etc. for each of the devices it controls?
>>>   For the time being we quickly hacked an xml-based scheme to get
>>>   this info upon installation, but it's clearly not suitable
>>>   for mainline.
>>>   
>>>       
>> I have written a test driver for a very old slave card we had lying  
>> around. In that case we used module parameters to determine the location  
>> of the device on the bus (it used a fixed VME address space). In this  
>> instance the interrupt level and ID could be configured in the registers  
>> available in the VME address space, hence I added module parameters to  
>> allow these to be configured. In this respect configuration of VME  
>> devices is very similar to ISA devices - neither of the buses has  
>> supported discovery mechanism from the outset and thus old cards. I  
>> therefore implemented a mechanism similar to how I believe ISA  
>> approaches this.
>>
>> The framework that I have proposed aims to provide a consistent API to  
>> manage allowing the resources provided by the VME bridges to be managed  
>> in as a consistent a manner as possible. I believe it is up to the  
>> device drivers for the devices found on the VME bus to determine the  
>> best way to configure this as it is not provided by the VME 
>> specifications.
>>     
>
> The problem is how to manage several (say 10) devices that have to be
> controlled with the same driver; passing parameters upon loading the
> driver's module doesn't seem to scale beyond one device..
I implemented it using param array, I agree that the arguments might get quite long with 10 devices, especially if multiple parameters are required, but I don't see why it shouldn't work.

> At the moment I see no clean way of doing this; right now we're doing
> it through an IOCTL, copying from user-space a tree that describes the
> devices to install, namely mappings and IRQs for each of them.
>
> Greg, any ideas on how to deal with this?
>
>   
>> For using the VME bus for communication between two SBCs, I think we  
>> could use the CRCSR space to provide some information about the  
>> resources used on each board for say, a virtual network driver like the  
>> rapidIO subsystem provides. Possibly using the "device tree" stuff used  
>> on PowerPC, Blackfin and Sparc archs (I believe) for passing device  
>> layout between the firmware and kernel at boot.
>>     
>
> hmm I don't see how we could implement this common consistent view of
> the bus in a way that could work with every bridge. For the time
> being I'd defer this and simply pass that responsibility to user-space.
>
>   
That's the conclusion I sort of came to. I guess how the VME address 
space(s) are used is a system integration issue.
>> Sure, np,
>>
>> Martyn
>>     
>
> Thanks,
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 15:53           ` Martyn Welch
@ 2009-08-10 16:26             ` Shawn Bohrer
  2009-08-10 19:38             ` Emilio G. Cota
  1 sibling, 0 replies; 65+ messages in thread
From: Shawn Bohrer @ 2009-08-10 16:26 UTC (permalink / raw)
  To: Martyn Welch
  Cc: Emilio G. Cota, Greg K-H, linux-kernel, devel, Sebastien Dugue

On Mon, Aug 10, 2009 at 04:53:39PM +0100, Martyn Welch wrote:
> Emilio G. Cota wrote:
>> Martyn Welch wrote:
>>   
>>> Emilio G. Cota wrote:
>>>     
>>>> - semaphores? isn't it screaming for mutexes?
>>>>       
>>> The semaphores are initialized in mutex mode.
>>>     
>>
>> I know, but then please use mutexes.
>>
>>   
> I'm clearly missing something here - can you provide me with an example?

The kernel now has native mutexes so there is no need to use semaphores
as mutexes.  Take a look at:

kernel/mutex.c

--
Shawn

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-09 12:17       ` Emilio G. Cota
  2009-08-10 13:13         ` Martyn Welch
@ 2009-08-10 16:28         ` Greg KH
  2009-08-10 20:05           ` Emilio G. Cota
  1 sibling, 1 reply; 65+ messages in thread
From: Greg KH @ 2009-08-10 16:28 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Martyn Welch, linux-kernel, devel, Sebastien Dugue

On Sun, Aug 09, 2009 at 02:17:16PM +0200, Emilio G. Cota wrote:
> (oops, just correcting Sebastien's e-mail address)
> 
> Emilio G. Cota wrote:
> > I remember we had a look at the vmelinux interface, but it really
> > was so limited that we decided to get rid of it altogether. Do you
> > really think it's worth bringing it back to life? Since it's never
> > been merged into the kernel, why don't we just get it right this time
> > and forget about it?
> > 
> > Instead of using that we implemented a heretic IOCTL-based
> > interface for user-space; at least with it you could create a
> > driver (with no interrupt support) for a device in user-space.

Why not just use the UIO interface instead of creating
yet-another-kernel/user-api?

Will that interface not work for what you want to use here?

thanks,

greg k-h

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 15:26           ` Emilio G. Cota
@ 2009-08-10 16:29             ` Greg KH
  2009-08-10 16:30             ` Martyn Welch
  1 sibling, 0 replies; 65+ messages in thread
From: Greg KH @ 2009-08-10 16:29 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Martyn Welch, linux-kernel, devel, Sebastien Dugue

On Mon, Aug 10, 2009 at 05:26:06PM +0200, Emilio G. Cota wrote:
> Martyn Welch wrote:
> >>> Instead of using that we implemented a heretic IOCTL-based
> >>> interface for user-space; at least with it you could create a
> [ snip ]
> >>> #define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)
> >>>
> > I am moving the interface in that direction, I remain unconvinced about  
> > the contents of your vme_mapping structure, it's too tsi-148 specific.
> 
> Could you please point out why is too tsi148-specific?
> 
> The point here is that the driver should know *nothing* about
> windows, etc. What it should just know is:
> - I want a mapping of a certain size to VME address X
> The struct provides exactly this.

So does the existing UIO interface, right?  Please don't reinvent the
wheel here :)

thanks,

greg k-h

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 15:26           ` Emilio G. Cota
  2009-08-10 16:29             ` Greg KH
@ 2009-08-10 16:30             ` Martyn Welch
  2009-08-10 20:36               ` Emilio G. Cota
  1 sibling, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 16:30 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>>>> Instead of using that we implemented a heretic IOCTL-based
>>>> interface for user-space; at least with it you could create a
>>>>         
> [ snip ]
>   
>>>> #define VME_IOCTL_START_DMA             _IOWR('V', 10, struct vme_dma)
>>>>
>>>>         
>> I am moving the interface in that direction, I remain unconvinced about  
>> the contents of your vme_mapping structure, it's too tsi-148 specific.
>>     
>
> Could you please point out why is too tsi148-specific?
>
> The point here is that the driver should know *nothing* about
> windows, etc. What it should just know is:
> - I want a mapping of a certain size to VME address X
>   

I'm not convinced. Given that each bridge provides a limited number of 
windows (some more than others), we are limited to how large a window we 
can produce (we need to map them somewhere) and the potential 
combinations are so great (independant 16, 32, 40, 64 and CR/CSR address 
spaces, not to mention access modes) it is important to assign the 
windows to a driver, so that it may move them as it sees fit. For 
example, supporting 10 devices (as you have mentioned earier) with a 
single driver could potentially require a single window that it knows it 
has exclusive use of to position over each devices register as required 
without either having to provide a large window (unfeasibly large on 
most/all platforms if they are scattered across the 64-bit address 
space) or needing more windows than are available on any of the bridges 
I have seen (8 being the maximum).

> The struct provides exactly this.
>   
Ah - vme_dma does to some degree. I was talking about vme_mapping for 
configuring vme windows, from your vmebus.h:

115 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l115> 
 * This data structure is used for describing both a hardware window
116 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l116> 
 * and a logical mapping on top of a hardware window. Therefore some of
117 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l117> 
 * the fields are only relevant to one of those two entities.
118 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l118> 
 */
119 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l119> 
struct vme_mapping {
120 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l120> 
        int     window_num;
121 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l121> 

122 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l122> 
        /* Reserved for kernel use */
123 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l123> 
        void    *kernel_va;
124 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l124>
125 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l125> 
        /* Reserved for userspace */
126 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l126> 
        void    *user_va;
127 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l127> 
        int     fd;
128 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l128> 

129 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l129> 
        /* Window settings */
130 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l130> 
        int                             window_enabled;
131 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l131> 
        enum vme_data_width             data_width;
132 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l132> 
        enum vme_address_modifier       am;
133 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l133> 
        int                             read_prefetch_enabled;
134 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l134> 
        enum vme_read_prefetch_size     read_prefetch_size;

Tsi-148 specific.

135 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l135> 
        enum vme_2esst_mode             v2esst_mode;
136 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l136> 
        int                             bcast_select;
137 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l137> 
        unsigned int                    pci_addru;
138 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l138> 
        unsigned int                    pci_addrl;

Why are these split, why not a single unsigned long long?

139 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l139> 
        unsigned int                    sizeu;
140 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l140> 
        unsigned int                    sizel;

Ditto.

141 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l141> 
        unsigned int                    vme_addru;
142 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l142> 
        unsigned int                    vme_addrl;

Ditto.

143 
<http://repo.or.cz/w/tsi148vmebridge.git?a=blob;f=driver/vmebus.h;h=6f25a7715a973ae07a6d1f71bdb15d638a56e7aa;hb=HEAD#l143> 
};

In addition your enum vme_address_modifier would need extending when 
specifying slave windows, the tsi-148 can support windows with USER and 
SUP access, as well as DATA and PRG. Why throw access privileges and 
address spaces together like that? The VME spec also specifies 4 User 
definable  address spaces...

I'm still not convinced by all these structures - you've defined tonnes 
of them, I don't feel that it aids readability and maintainability at all.

> Also, could you please send me (off-list) documentation of the
> Universe bridge? It'd be useful for the work on the generic layer.
>
>   
It's all over the web :-) We've had it for years and I'm not sure under 
what terms we originally got it, so I'm afraid I've got to assume we're 
still bound under some NDA, sorry. Searching google for "Universe II 
Manual" much get you what you want though...

Martyn

> E.
>   



-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-08 23:01     ` Emilio G. Cota
  2009-08-10 12:44       ` Martyn Welch
@ 2009-08-10 16:30       ` Greg KH
  1 sibling, 0 replies; 65+ messages in thread
From: Greg KH @ 2009-08-10 16:30 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: linux-kernel, devel, Martyn Welch, Sebastien Dugue

On Sun, Aug 09, 2009 at 01:01:45AM +0200, Emilio G. Cota wrote:
> A few comments after a quick glance, will need to dig deeper
> though:
> - semaphores? isn't it screaming for mutexes?

Probably, it's old code forward-ported.  Patches gladly welcome.

thanks,

greg k-h

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 15:31           ` Emilio G. Cota
@ 2009-08-10 16:40             ` Martyn Welch
  2009-08-10 19:50               ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 16:40 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Emilio G. Cota wrote:
>   
>> I've had a closer look; it seems to me that most of it is unnecessary;
>> there's no show those lists to a driver. I'd just provide a single
>> 'do_dma(attributes)' call that sleeps until it's done (or similar).
>>     
>
> s/there's no show/there's no point in showing/
>
> Basically the driver simply shouldn't care about these DMA
> lists. It just wants to get the work done, no matter how.
>
> E.
>   
I have provided helper functions to aid with filling out structures that 
define the attributes, these are then pointed to by a generic DMA 
transfer structure. Bar the helper functions to aid in completing these 
commonly filled structures, at this level I can't see much difference. 
However rather than targeting a the "single shot" operation of the DMA 
controller I have provided the ability for the driver writer to use the 
link-list mode.

If the driver writer wants a "single shot" effect, there's little 
overhead in creating a one item link-list and executing it. However, 
without support for this mode the author is stuck with single shot 
transfers.  If the author wants to perform scatter gather type 
transfers, without this functionality in the API - which is supported by 
all the bridges I have seen - he won't be able to easily avail himself 
of this functionality and get "work done".

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [PATCH] Staging: vme: fix {master,slave}_get check bug
  2009-08-10 14:52     ` [PATCH] Staging: vme: fix {master,slave}_get check bug Emilio G. Cota
@ 2009-08-10 16:50       ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-10 16:50 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel

Emilio G. Cota wrote:
> It's checking the wrong functions; fix it.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
>   
Acked-by: Martyn Welch <martyn.welch@gefanuc.com>
> ---
>  drivers/staging/vme/vme.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/staging/vme/vme.c b/drivers/staging/vme/vme.c
> index 8ee1192..ed2eeb0 100644
> --- a/drivers/staging/vme/vme.c
> +++ b/drivers/staging/vme/vme.c
> @@ -338,7 +338,7 @@ int vme_slave_get (struct vme_resource *resource, int *enabled,
>  
>  	image = list_entry(resource->entry, struct vme_slave_resource, list);
>  
> -	if (bridge->slave_set == NULL) {
> +	if (bridge->slave_get == NULL) {
>  		printk("vme_slave_get not supported\n");
>  		return -EINVAL;
>  	}
> @@ -499,7 +499,7 @@ int vme_master_get (struct vme_resource *resource, int *enabled,
>  
>  	image = list_entry(resource->entry, struct vme_master_resource, list);
>  
> -	if (bridge->master_set == NULL) {
> +	if (bridge->master_get == NULL) {
>  		printk("vme_master_set not supported\n");
>  		return -EINVAL;
>  	}
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 15:53           ` Martyn Welch
  2009-08-10 16:26             ` Shawn Bohrer
@ 2009-08-10 19:38             ` Emilio G. Cota
  2009-08-11  8:29               ` Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 19:38 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> That's one reason for the helper function. If VME bridges are added  
> which sit of on other buses then vme_alloc_consistent can be extended to  
> support this without requiring VME device drivers to be altered to  
> reflect the fact that pci_alloc_consistent might not work for all VME  
> bridges.

But it corresponds to the bridge's code (not to the generic layer) to
know whether the bridge sits on top of PCI or not; hence my comment.

> The DMA controller in the tsi-148 can do PCI to PCI; PCI to VME; VME to  
> PCI; VME to VME; Patterns to  VME and Patterns to PCI transfers. How do  
> you specify all those options without providing a structure where over  
> 50% of the fields aren't used for any given transfer?

I think a struct where not _every_ field is *always* used is fine.
However requiring your drivers to know about what your bridge
looks like is a mistake; just give them something thin that doesn't
assume anything about what's behind it, and if it's not supported,
return an appropriate error message.

> Every bridge I have seen is capable of link-list execution. The API  
> provides the ability to do scatter-gather style DMA transfers, this  
> could be used as part of a "zero-copy" type driver for high speed VME  
> capture devices. How would you support the link-list execution with a  
> single call?

Let me say it again: "Drivers should know *nothing* about the
underlying bridge".
They should work with *any* bridge; or do we care about the PCI
bridge we're plug our PCI device on when writing a driver for it?

> I was also looking at the ability to queue DMA transfers if the  
> controller was currently busy - if the consensus is that this is  
> overkill I will happily remove this.

AFAICT the tsi148 has several DMA channels; in our driver we just
try to find a free channel to proceed with the transfer; if there
aren't any then we return EBUSY.
Drawing an analogy with current 'streaming DMA' transfers, we're
told we should check whether they failed or not, so I think
trying to be too clever here it's not worth it.

> I implemented it using param array, I agree that the arguments might get
> quite long with 10 devices, especially if multiple parameters are
> required, but I don't see why it shouldn't work.

The fact that 'it works' doesn't make it less ugly or messy.
When referring to code, these two words usually mean "there's
a better way"; so let's just find it.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 16:40             ` Martyn Welch
@ 2009-08-10 19:50               ` Emilio G. Cota
  2009-08-11  8:02                 ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 19:50 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> If the driver writer wants a "single shot" effect, there's little  
> overhead in creating a one item link-list and executing it. However,  
> without support for this mode the author is stuck with single shot  
> transfers.  If the author wants to perform scatter gather type  
> transfers, without this functionality in the API - which is supported by  
> all the bridges I have seen - he won't be able to easily avail himself  
> of this functionality and get "work done".

Again, he (the driver writer) should know *nothing* about that.

As for the scatter-gather case, what should happen is:

User:		hey, there's a buffer I want to transfer. here's
		a pointer and size
VME layer:	ok, passing it to the bridge
VME bridge:	I know I'm over PCI, so I'll get the pages
		from memory and map the sg list over PCI, and
		then will tell the bridge to perform from there.

That's just a particular example, but for the user (read driver
writer) it's much simpler this way. Doesn't need to know about
lists or whatever.

E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 16:28         ` Greg KH
@ 2009-08-10 20:05           ` Emilio G. Cota
  2009-08-10 21:09             ` Greg KH
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 20:05 UTC (permalink / raw)
  To: Greg KH; +Cc: Martyn Welch, linux-kernel, devel, Sebastien Dugue

Greg KH wrote:
> On Sun, Aug 09, 2009 at 02:17:16PM +0200, Emilio G. Cota wrote:
> > > Instead of using that we implemented a heretic IOCTL-based
> > > interface for user-space; at least with it you could create a
> > > driver (with no interrupt support) for a device in user-space.
> 
> Why not just use the UIO interface instead of creating
> yet-another-kernel/user-api?
> 
> Will that interface not work for what you want to use here?

Certainly. I didn't mean we'd like to see that merged. Currently
we make extensive use of IOCTLs because we are maintaining
kernel and user-space code for two platforms/OSes (ppc-lynx and
x86-linux), and not having access to lynx' source code doesn't
help. So IOCTLs are pretty much everywhere, much to my regret.

E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 16:30             ` Martyn Welch
@ 2009-08-10 20:36               ` Emilio G. Cota
  2009-08-11  9:03                 ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-10 20:36 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Emilio G. Cota wrote:
>> Martyn Welch wrote:
>> The point here is that the driver should know *nothing* about
>> windows, etc. What it should just know is:
>> - I want a mapping of a certain size to VME address X
>>   
>
> I'm not convinced. Given that each bridge provides a limited number of  
> windows (some more than others), we are limited to how large a window we  
> can produce (we need to map them somewhere) and the potential  
> combinations are so great (independant 16, 32, 40, 64 and CR/CSR address  
> spaces, not to mention access modes) it is important to assign the  
> windows to a driver, so that it may move them as it sees fit. For  
> example, supporting 10 devices (as you have mentioned earier) with a  
> single driver could potentially require a single window that it knows it  
> has exclusive use of to position over each devices register as required  
> without either having to provide a large window (unfeasibly large on  
> most/all platforms if they are scattered across the 64-bit address  
> space) or needing more windows than are available on any of the bridges  
> I have seen (8 being the maximum).

No, it corresponds to the bridge to manage *its* resources. Drivers
would have a very hard time, because they'd need to know their
own resources but _also_ the use other drivers are making of the
bridge. And that's madness at its best.

>> The struct provides exactly this.
>>   
> Ah - vme_dma does to some degree. I was talking about vme_mapping for  
> configuring vme windows, from your vmebus.h:
>

[ was it just me or some lines + links got copied? I've removed them
anyway ]

> * This data structure is used for describing both a hardware window
> * and a logical mapping on top of a hardware window. Therefore some of
> * the fields are only relevant to one of those two entities.
> */
> struct vme_mapping {
>        int     window_num;
>

[ snip ]
>        enum vme_read_prefetch_size     read_prefetch_size;
> Tsi-148 specific.

If others don't implement it, it's ok; however you need to
cover all the cases, so it's needed here.

[ snip ]
>        int                             bcast_select;
>        unsigned int                    pci_addru;
>        unsigned int                    pci_addrl;
>
> Why are these split, why not a single unsigned long long?
>        unsigned int                    sizeu;
>        unsigned int                    sizel;
> Ditto.
>        unsigned int                    vme_addru;
>        unsigned int                    vme_addrl;
> Ditto.

- Most accesses are 32-bit accesses. Treating all of them
  as 64-bit accesses would decrease performance for most of
  them--which happen to be 32-bit.

> In addition your enum vme_address_modifier would need extending when  
> specifying slave windows, the tsi-148 can support windows with USER and  
> SUP access, as well as DATA and PRG. Why throw access privileges and  
> address spaces together like that? The VME spec also specifies 4 User  
> definable  address spaces...

Well Sebastien worked on this for less than two months, and to be
honest with you the result is much better than the original code
we had from Motorola--which is the one you based your work on,
I think.
So yes, the whole spec is not there, but
- If no one uses something, there's no point in implementing it--
  irrespective of being written on a spec or not.
- Sebastien did a bloody good job, but obviously he focused on
  our urgent matters, i.e. with the Motorola driver we couldn't
  go on production with our machines.

> I'm still not convinced by all these structures - you've defined tonnes  
> of them, I don't feel that it aids readability and maintainability at 
> all.
Tons of them? Seriously?

t61$ /data/src/linux-next/include/linux ->cat pci*.h | egrep '^struct' | wc -l
41
t61$ /data/src/linux-next/include/linux ->cat vme*.h | egrep '^struct' | wc -l
6

I would call that pretty sane as a starting point.

[ NB. this is my current dev tree, vme.h is just 'our' vmebus.h
  Lynx's compatility crap removed ]

> It's all over the web :-) We've had it for years and I'm not sure under  
> what terms we originally got it, so I'm afraid I've got to assume we're  
> still bound under some NDA, sorry. Searching google for "Universe II  
> Manual" much get you what you want though...

o rite, no probs.

Thanks,
E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 20:05           ` Emilio G. Cota
@ 2009-08-10 21:09             ` Greg KH
  2009-08-11  7:04               ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Greg KH @ 2009-08-10 21:09 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Martyn Welch, linux-kernel, devel, Sebastien Dugue

On Mon, Aug 10, 2009 at 10:05:26PM +0200, Emilio G. Cota wrote:
> Greg KH wrote:
> > On Sun, Aug 09, 2009 at 02:17:16PM +0200, Emilio G. Cota wrote:
> > > > Instead of using that we implemented a heretic IOCTL-based
> > > > interface for user-space; at least with it you could create a
> > > > driver (with no interrupt support) for a device in user-space.
> > 
> > Why not just use the UIO interface instead of creating
> > yet-another-kernel/user-api?
> > 
> > Will that interface not work for what you want to use here?
> 
> Certainly. I didn't mean we'd like to see that merged. Currently
> we make extensive use of IOCTLs because we are maintaining
> kernel and user-space code for two platforms/OSes (ppc-lynx and
> x86-linux), and not having access to lynx' source code doesn't
> help. So IOCTLs are pretty much everywhere, much to my regret.

That does not mean we will accept adding new ioctls for the Linux
drivers, so I fail to see the point of this :)

thanks,

greg k-h

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 12:44       ` Martyn Welch
  2009-08-10 14:14         ` Emilio G. Cota
@ 2009-08-11  4:54         ` Mike Frysinger
  2009-08-11  7:48           ` Martyn Welch
  1 sibling, 1 reply; 65+ messages in thread
From: Mike Frysinger @ 2009-08-11  4:54 UTC (permalink / raw)
  To: Martyn Welch
  Cc: Emilio G. Cota, Greg K-H, linux-kernel, devel, Sebastien Dugue

On Mon, Aug 10, 2009 at 08:44, Martyn Welch wrote:
> For using the VME bus for communication between two SBCs, I think we could
> use the CRCSR space to provide some information about the resources used on
> each board for say, a virtual network driver like the rapidIO subsystem
> provides. Possibly using the "device tree" stuff used on PowerPC, Blackfin
> and Sparc archs (I believe) for passing device layout between the firmware
> and kernel at boot.

Blackfin doesnt use device trees and there are no plans to do so
-mike

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 21:09             ` Greg KH
@ 2009-08-11  7:04               ` Emilio G. Cota
  0 siblings, 0 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11  7:04 UTC (permalink / raw)
  To: Greg KH; +Cc: Martyn Welch, linux-kernel, devel, Sebastien Dugue

Greg KH wrote:
> On Mon, Aug 10, 2009 at 10:05:26PM +0200, Emilio G. Cota wrote:
> > Greg KH wrote:
> > > On Sun, Aug 09, 2009 at 02:17:16PM +0200, Emilio G. Cota wrote:
> > > > > Instead of using that we implemented a heretic IOCTL-based
> > > > > interface for user-space; at least with it you could create a
> > > > > driver (with no interrupt support) for a device in user-space.
> > > 
> > > Why not just use the UIO interface instead of creating
> > > yet-another-kernel/user-api?
> > > 
> > > Will that interface not work for what you want to use here?
> > 
> > Certainly. I didn't mean we'd like to see that merged. Currently
> > we make extensive use of IOCTLs because we are maintaining
> > kernel and user-space code for two platforms/OSes (ppc-lynx and
> > x86-linux), and not having access to lynx' source code doesn't
> > help. So IOCTLs are pretty much everywhere, much to my regret.
> 
> That does not mean we will accept adding new ioctls for the Linux
> drivers, so I fail to see the point of this :)

I showed it just to point out that the vmelinux.org interface was
not very useful--I never said I'd like to see these IOCTLs merged.

I do *not* want to see those IOCTLs anywhere outside of our code,
and never did. Sorry for the confusion; hope it's clear now.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11  4:54         ` Mike Frysinger
@ 2009-08-11  7:48           ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-11  7:48 UTC (permalink / raw)
  To: Mike Frysinger
  Cc: Emilio G. Cota, Greg K-H, linux-kernel, devel, Sebastien Dugue

Mike Frysinger wrote:
> On Mon, Aug 10, 2009 at 08:44, Martyn Welch wrote:
>   
>> For using the VME bus for communication between two SBCs, I think we could
>> use the CRCSR space to provide some information about the resources used on
>> each board for say, a virtual network driver like the rapidIO subsystem
>> provides. Possibly using the "device tree" stuff used on PowerPC, Blackfin
>> and Sparc archs (I believe) for passing device layout between the firmware
>> and kernel at boot.
>>     
>
> Blackfin doesnt use device trees and there are no plans to do so
> -mike
>   
Sorry, my mistake, I thought it did.

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 19:50               ` Emilio G. Cota
@ 2009-08-11  8:02                 ` Martyn Welch
  2009-08-11  8:43                   ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11  8:02 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> If the driver writer wants a "single shot" effect, there's little  
>> overhead in creating a one item link-list and executing it. However,  
>> without support for this mode the author is stuck with single shot  
>> transfers.  If the author wants to perform scatter gather type  
>> transfers, without this functionality in the API - which is supported by  
>> all the bridges I have seen - he won't be able to easily avail himself  
>> of this functionality and get "work done".
>>     
>
> Again, he (the driver writer) should know *nothing* about that.
>
> As for the scatter-gather case, what should happen is:
>
> User:		hey, there's a buffer I want to transfer. here's
> 		a pointer and size
>   

Buffer where? On VME or PCI, where are you transferring it to? The API I 
have will allow scattered locations on VME to be copied to PCI and 
visa-versa, scattered locations on PCI to be copied to VME and 
visa-versa. Even scattered locations on VME to scattered locations on 
PCI with a bit of effort.

> VME layer:	ok, passing it to the bridge
> VME bridge:	I know I'm over PCI, so I'll get the pages
> 		from memory and map the sg list over PCI, and
> 		then will tell the bridge to perform from there.
>
> That's just a particular example, but for the user (read driver
> writer) it's much simpler this way. Doesn't need to know about
> lists or whatever.
>   

But seems to fail to provide the flexibility that I outlined above.

Martyn
> E.
>   

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-10 19:38             ` Emilio G. Cota
@ 2009-08-11  8:29               ` Martyn Welch
  2009-08-11 14:49                 ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11  8:29 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> That's one reason for the helper function. If VME bridges are added  
>> which sit of on other buses then vme_alloc_consistent can be extended to  
>> support this without requiring VME device drivers to be altered to  
>> reflect the fact that pci_alloc_consistent might not work for all VME  
>> bridges.
>>     
>
> But it corresponds to the bridge's code (not to the generic layer) to
> know whether the bridge sits on top of PCI or not; hence my comment.
>
>   
>> The DMA controller in the tsi-148 can do PCI to PCI; PCI to VME; VME to  
>> PCI; VME to VME; Patterns to  VME and Patterns to PCI transfers. How do  
>> you specify all those options without providing a structure where over  
>> 50% of the fields aren't used for any given transfer?
>>     
>
> I think a struct where not _every_ field is *always* used is fine.
> However requiring your drivers to know about what your bridge
> looks like is a mistake; just give them something thin that doesn't
> assume anything about what's behind it, and if it's not supported,
> return an appropriate error message.
>   

That's what the API does - how does it require the driver writer to know 
about what the bridge look like. All that it requires is the the 
hardware supports the transfer mode that the driver requests, however 
that's always going to be an issue with multiple devices with different 
properties.

>> Every bridge I have seen is capable of link-list execution. The API  
>> provides the ability to do scatter-gather style DMA transfers, this  
>> could be used as part of a "zero-copy" type driver for high speed VME  
>> capture devices. How would you support the link-list execution with a  
>> single call?
>>     
>
> Let me say it again: "Drivers should know *nothing* about the
> underlying bridge".
> They should work with *any* bridge; or do we care about the PCI
> bridge we're plug our PCI device on when writing a driver for it?
>
>   

Again: what part of the API I have defined forces the driver to know 
about the underlying bridge?

>> I was also looking at the ability to queue DMA transfers if the  
>> controller was currently busy - if the consensus is that this is  
>> overkill I will happily remove this.
>>     
>
> AFAICT the tsi148 has several DMA channels; in our driver we just
> try to find a free channel to proceed with the transfer; if there
> aren't any then we return EBUSY.
>   

Where as I request a DMA channel - a resource is returned, the driver 
does not need to know which one, he has the resource structure which 
refers to it. It treats the DMA engines as a resource, just as it treats 
the windows, both master and slave as resources.

> Drawing an analogy with current 'streaming DMA' transfers, we're
> told we should check whether they failed or not, so I think
> trying to be too clever here it's not worth it.
>
>   
Yes - my driver checks to see of the transfer has succeeded, I don't see 
quite what this has to do with the API. How's the API being clever?
>> I implemented it using param array, I agree that the arguments might get
>> quite long with 10 devices, especially if multiple parameters are
>> required, but I don't see why it shouldn't work.
>>     
>
> The fact that 'it works' doesn't make it less ugly or messy.
> When referring to code, these two words usually mean "there's
> a better way"; so let's just find it.
>
> E.
>   
If there's a better way to do this then fine - I'm happy to use it, 
however I'm not quite sure how this relates to the API, core VME code or 
low-level drivers. I think it's up to the authors of the VME device 
drivers themselves how they approach this. The API uses the standard 
bind-table approach as best as I could. If you can think of a better 
approach to this, patches will be welcome.

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11  8:02                 ` Martyn Welch
@ 2009-08-11  8:43                   ` Emilio G. Cota
  0 siblings, 0 replies; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11  8:43 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Emilio G. Cota wrote:
>> Again, he (the driver writer) should know *nothing* about that.
>>
>> As for the scatter-gather case, what should happen is:
>>
>> User:		hey, there's a buffer I want to transfer. here's
>> 		a pointer and size
> Buffer where? On VME or PCI, where are you transferring it to? The API I  
> have will allow scattered locations on VME to be copied to PCI and  
> visa-versa, scattered locations on PCI to be copied to VME and  
> visa-versa. Even scattered locations on VME to scattered locations on  
> PCI with a bit of effort.

Admittedly our current tsi148 driver deals with the most common
situation: DMA from main (master) CPU RAM to/from a VME slave.

I'm not claiming we shouldn't support sg lists; however the
implementation should resemble the existing sg interfaces.
I'm currently working on something along those lines.

Thanks,
E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-10 20:36               ` Emilio G. Cota
@ 2009-08-11  9:03                 ` Martyn Welch
  2009-08-11  9:40                   ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11  9:03 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Emilio G. Cota wrote:
>>     
>>> Martyn Welch wrote:
>>> The point here is that the driver should know *nothing* about
>>> windows, etc. What it should just know is:
>>> - I want a mapping of a certain size to VME address X
>>>   
>>>       
>> I'm not convinced. Given that each bridge provides a limited number of  
>> windows (some more than others), we are limited to how large a window we  
>> can produce (we need to map them somewhere) and the potential  
>> combinations are so great (independant 16, 32, 40, 64 and CR/CSR address  
>> spaces, not to mention access modes) it is important to assign the  
>> windows to a driver, so that it may move them as it sees fit. For  
>> example, supporting 10 devices (as you have mentioned earier) with a  
>> single driver could potentially require a single window that it knows it  
>> has exclusive use of to position over each devices register as required  
>> without either having to provide a large window (unfeasibly large on  
>> most/all platforms if they are scattered across the 64-bit address  
>> space) or needing more windows than are available on any of the bridges  
>> I have seen (8 being the maximum).
>>     
>
> No, it corresponds to the bridge to manage *its* resources. Drivers
> would have a very hard time, because they'd need to know their
> own resources but _also_ the use other drivers are making of the
> bridge. And that's madness at its best.
>   

I think you've missed how the current API works - drivers request a 
resource, not a specific window, specifying the attributes they wish the 
window to support, such as "A32 address space", "2eSST transfer mode". 
The VME core looks at the resources it has available and returns a 
resource structure that identifies the underlying resource. The driver 
has no need to know which one it is. If a suitable resource is not 
available, no resource is returned, the underlying hardware is therefore 
incapable of providing the required attributes or all the resources are 
in use.

In terms of knowing "the use other drivers are making of the bridge", if 
to drivers try to create a slave window using the same address space and 
location there is no way, beyond basic checks that windows don't overlap 
and rejecting the second attempt, that anything can be done. The bus 
simply can't support this.

The API I have defined provides basic resource management - may I 
suggest if you want something more complex that allows the user to just 
pick a location/size and not worry about windows at all that you provide 
a patch for this that layers above the basic resource management? This 
would then be of use to anyone that wanted to use it for any underlying 
VME bridge.
>   
>>> The struct provides exactly this.
>>>   
>>>       
>> Ah - vme_dma does to some degree. I was talking about vme_mapping for  
>> configuring vme windows, from your vmebus.h:
>>
>>     
>
> [ was it just me or some lines + links got copied? I've removed them
> anyway ]
>
>   
>> * This data structure is used for describing both a hardware window
>> * and a logical mapping on top of a hardware window. Therefore some of
>> * the fields are only relevant to one of those two entities.
>> */
>> struct vme_mapping {
>>        int     window_num;
>>
>>     
>
> [ snip ]
>   
>>        enum vme_read_prefetch_size     read_prefetch_size;
>> Tsi-148 specific.
>>     
>
> If others don't implement it, it's ok; however you need to
> cover all the cases, so it's needed here.
>
> [ snip ]
>   
>>        int                             bcast_select;
>>        unsigned int                    pci_addru;
>>        unsigned int                    pci_addrl;
>>
>> Why are these split, why not a single unsigned long long?
>>        unsigned int                    sizeu;
>>        unsigned int                    sizel;
>> Ditto.
>>        unsigned int                    vme_addru;
>>        unsigned int                    vme_addrl;
>> Ditto.
>>     
>
> - Most accesses are 32-bit accesses. Treating all of them
>   as 64-bit accesses would decrease performance for most of
>   them--which happen to be 32-bit.
>
>   
I'm not - I'm storing them as 64-bit values, which they are, in the 
structures used in *software*. These are then split *when* a write to 
the hardware registers is required. Similarly, when the registers are 
occasionally read they are combined and stored as a 64-bit value. This 
simplifies all *software* checking and manipulation. By storing these as 
2 32-bit values every driver that uses the VME core will need to convert 
pci addresses, vme addresses and counts to 2 32-bit values. That is madness.
>> In addition your enum vme_address_modifier would need extending when  
>> specifying slave windows, the tsi-148 can support windows with USER and  
>> SUP access, as well as DATA and PRG. Why throw access privileges and  
>> address spaces together like that? The VME spec also specifies 4 User  
>> definable  address spaces...
>>     
>
> Well Sebastien worked on this for less than two months, and to be
> honest with you the result is much better than the original code
> we had from Motorola--which is the one you based your work on,
> I think.
>   
So that's where the 2 32-bit values come from. That was an artifact of 
the Motorola driver mapping the structure over the registers. I'm 
surprised you kept that, it was practically the first thing I corrected.
> So yes, the whole spec is not there, but
> - If no one uses something, there's no point in implementing it--
>   irrespective of being written on a spec or not.
> - Sebastien did a bloody good job, but obviously he focused on
>   our urgent matters, i.e. with the Motorola driver we couldn't
>   go on production with our machines.
>
>   
Right - but I've taken a higher level view of what a VME API should 
provide, with the aim of providing an API that will support the features 
described in the VME specifications for more than one chip set, rather 
than targeting just those features that I need right now. I have 
provided a VME core to enable common checking routines and functionality 
to be provided to all VME bridge drivers without duplication. I too feel 
that I have done a good job and, whilst I am not going to make claims 
about it's readiness for production machines at this stage, feel that 
the tsi-148 driver that I have adapted/written is quite robust.
>> I'm still not convinced by all these structures - you've defined tonnes  
>> of them, I don't feel that it aids readability and maintainability at 
>> all.
>>     
> Tons of them? Seriously?
>
> t61$ /data/src/linux-next/include/linux ->cat pci*.h | egrep '^struct' | wc -l
> 41
> t61$ /data/src/linux-next/include/linux ->cat vme*.h | egrep '^struct' | wc -l
> 6
>
> I would call that pretty sane as a starting point.
>
> [ NB. this is my current dev tree, vme.h is just 'our' vmebus.h
>   Lynx's compatility crap removed ]
>
>   
>> It's all over the web :-) We've had it for years and I'm not sure under  
>> what terms we originally got it, so I'm afraid I've got to assume we're  
>> still bound under some NDA, sorry. Searching google for "Universe II  
>> Manual" much get you what you want though...
>>     
>
> o rite, no probs.
>
> Thanks,
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-11  9:03                 ` Martyn Welch
@ 2009-08-11  9:40                   ` Emilio G. Cota
  2009-08-11 12:46                     ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11  9:40 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> suggest if you want something more complex that allows the user to just  
> pick a location/size and not worry about windows at all

That's exactly the whole point. I think each bridge should manage
its resources; putting this on the upper layer would mean the
layer should have a mechanism of 'discovering' what the bridge
can/can't do. Anyway this could be revisited later.
I'm preparing a patch for this.

>> - Most accesses are 32-bit accesses. Treating all of them
>>   as 64-bit accesses would decrease performance for most of
>>   them--which happen to be 32-bit.
> I'm not - I'm storing them as 64-bit values, which they are, in the  
> structures used in *software*. These are then split *when* a write to  
> the hardware registers is required. Similarly, when the registers are  
> occasionally read they are combined and stored as a 64-bit value. This  
> simplifies all *software* checking and manipulation. By storing these as  
> 2 32-bit values every driver that uses the VME core will need to convert  
> pci addresses, vme addresses and counts to 2 32-bit values. That is 
> madness.

I agree with you on that's painful for doing 64-bit accesses.
However I'm still not convinced on the performance side (I mean
software), since most of the time the upper 32bits will be empty.
Will have a look though.

Cheers,
E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-11  9:40                   ` Emilio G. Cota
@ 2009-08-11 12:46                     ` Martyn Welch
  2009-08-11 21:01                       ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11 12:46 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> suggest if you want something more complex that allows the user to just  
>> pick a location/size and not worry about windows at all
>>     
>
> That's exactly the whole point. I think each bridge should manage
> its resources; putting this on the upper layer would mean the
> layer should have a mechanism of 'discovering' what the bridge
> can/can't do. Anyway this could be revisited later.
> I'm preparing a patch for this.
>   

I disagree. The bridge drivers should register their resources with the 
core. The core, or a layer above it, can control how those resources are 
used. This moves the complexity you want for managing the windows to a 
level that will work on all underlying drivers rather than having to be 
written explicitly for each one. The mechanism I have provided does this 
discovery.
>   
>>> - Most accesses are 32-bit accesses. Treating all of them
>>>   as 64-bit accesses would decrease performance for most of
>>>   them--which happen to be 32-bit.
>>>       
>> I'm not - I'm storing them as 64-bit values, which they are, in the  
>> structures used in *software*. These are then split *when* a write to  
>> the hardware registers is required. Similarly, when the registers are  
>> occasionally read they are combined and stored as a 64-bit value. This  
>> simplifies all *software* checking and manipulation. By storing these as  
>> 2 32-bit values every driver that uses the VME core will need to convert  
>> pci addresses, vme addresses and counts to 2 32-bit values. That is 
>> madness.
>>     
>
> I agree with you on that's painful for doing 64-bit accesses.
> However I'm still not convinced on the performance side (I mean
> software), since most of the time the upper 32bits will be empty.
> Will have a look though.
>
>   
Regardless of the contents of the upper 32 bits, they will need to be 
checked. For example when ensuring a window offset and size fit within a 
given address space. To do this the bound needs to be  calculated.

To do this with 2 32-bit values. add the lower window offset and size, 
which could lead to an over-flow of the lower 32-bit value, which needs 
to be added to the upper 32-bit value, which is the sum of the upper 
window offset and size. Then potentially check both the upper and lower 
values (thinking of 16-bit address range here) to ensure they are below 
the maximum address for the address range.

Compared to an addition of the window offset and size and a comparison 
to the maximum address for the address range when using one 64-bit value.

Clearly neither of the above basic outlines check that the combination 
has wrapped over 64 bits, but this would affect both equally.

So, which is more complex?

Martyn
> Cheers,
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11  8:29               ` Martyn Welch
@ 2009-08-11 14:49                 ` Emilio G. Cota
  2009-08-11 15:10                   ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11 14:49 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Again: what part of the API I have defined forces the driver to know  
> about the underlying bridge?

Let me answer with another question, maybe we get to understand
each other this way:

Could you please explain me what happens when 17 different drivers
want to control 17 different devices, each on a different slot,
same address modifier, only 1MB per device? Apply if necessary
to the tsi148 bridge.

NB1. Any given driver knows nothing about the other devices
on the crate; each driver only knows about the address, am and
size of the mapping for the device it controls--user-space
provide this info on a per-driver basis.
NB2. The mapping offsets configured through the cards' pins match
the information passed from user space to each of the cards.

Cheers,
E.

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

* Re: [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridgedriver
  2009-08-09  0:09     ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Emilio G. Cota
@ 2009-08-11 14:59       ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-11 14:59 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Hi Emilio,

Sorry for not replying to this mail sooner - got caught in my junk 
folder for some reason.

Emilio G. Cota wrote:
>
> Greg, Martyn,
>
> please have a look at our tsi148 driver, available at:
> http://repo.or.cz/w/tsi148vmebridge.git
> Whenever you encounter a reference to 'CES', ignore it,
> it's a CERN thing for easing our lives porting drivers from
> LynxOS.
>

I've had a brief look - it seem's to provide roughtly what I am 
attempting to provide with the tsi-148 driver as part of the VME stuff 
in staging.
>
>
> I could post the driver to LKML for review if you want; I would
> remove that CES crap before posting. I put this off because
> I knew it would never get merged before there was some generic
> vme glue supporting it. However now we're getting there so
> it's probably a good time to get cracking with this task.
>
> Martyn, a few comments about your driver, haven't had much
> time to dig very deep.
>
> - again, use mutexes instead of semaphores
>

Yes - I have a patch on the way.

> - The DMA code looks rather messy; I mean stuff like this:
>

That's because it's incomplete - hence why it's in the staging tree.
>
> > +#if 0
> > +     /* XXX Still todo */
> > +     for (x = 0; x < 8; x++) {       /* vme block size */
> > +             if ((32 << x) >= vmeDma->maxVmeBlockSize) {
> > +                     break;
> > +             }
> > +     }
> > +     if (x == 8)
> > +             x = 7;
> > +     dctlreg |= (x << 12);
> > +
> > +     for (x = 0; x < 8; x++) {       /* pci block size */
> > +             if ((32 << x) >= vmeDma->maxPciBlockSize) {
> > +                     break;
> > +             }
> > +     }
> > +     if (x == 8)
> > +             x = 7;
> > +     dctlreg |= (x << 4);
> > +
> > +     if (vmeDma->vmeBackOffTimer) {
> and the ifdefs 0..
>
> - correct me if I'm wrong, but does the slave need to know the
>   window number he wants for his device? See:
> > +int tsi148_slave_get(struct vme_slave_resource *image, int *enabled,
> > +     unsigned long long *vme_base, unsigned long long *size,
> > +     dma_addr_t *pci_base, vme_address_t *aspace, vme_cycle_t *cycle)
> > +{
> > +     unsigned int i, granularity = 0, ctl = 0;
> > +     unsigned int vme_base_low, vme_base_high;
> > +     unsigned int vme_bound_low, vme_bound_high;
> > +     unsigned int pci_offset_low, pci_offset_high;
> > +     unsigned long long vme_bound, pci_offset;
> > +
> > +
> > +     i = image->number;
>
> If that's the case, this is totally broken; in fact, even if you
> kept a list somewhere up on the generic vme layer keeping track
> of the already used windows (images?) to avoid any clashes,
>
Yes - the core layer keeps track of what is in use.

> in practice you'd very soon run out of windows: 8 modules, 8
> mappings, 8 windows. And you're dead.
>

Depends how the drivers use them.

>
> In a crate we have up to 20 modules, each of them with at least
> one address space to be mapped. To make this work we do the
> following:
>
> * The tsi148 has 1GB of address space available for mapping,
>   which is further divided into 8 windows, with a unique address
>   modifier per window.
> * Say you have 15 modules that need to map 1MB each, all of them
>   with the same address modifier (AM).
> Then what the slave driver requests is *not* a window, is what we
> call a 'vme mapping', which means "do whatever you want but give
> me a kernel address that points at this VME address, mapping with
> this size and AM".
>

With the API I have presented you could still map all 15 modules with 
one window in your driver if that makes sense.

>
> Our tsi148 driver notices then that there's already a window for
> that AM, so it just appends the mapping to the existing window.
> So at the end of the day only one window is used for all those
> mappings, which are nicely piled together.
>

If you want this kind of control, I think it would work well as a layer 
above the basic resource management built into the API I have. The two 
could live fairly happily side-by-side.

>
> Please clarify me how this is handled in your driver. The
> bottom line is to ensure that
> a) Many different modules can peacefully co-exist on a crate
>

Drivers request resources, if the resources are available, they get 
them. Resources are requested with respect to the attributes required.

> b) Drivers should know nothing about what else is on your crate
>

I agree.

Martyn
>
>
> E.
>
-- 

Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 14:49                 ` Emilio G. Cota
@ 2009-08-11 15:10                   ` Martyn Welch
  2009-08-11 15:36                     ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11 15:10 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Again: what part of the API I have defined forces the driver to know  
>> about the underlying bridge?
>>     
>
> Let me answer with another question, maybe we get to understand
> each other this way:
>
> Could you please explain me what happens when 17 different drivers
> want to control 17 different devices, each on a different slot,
> same address modifier, only 1MB per device? Apply if necessary
> to the tsi148 bridge.
>   

Not the same question, but I'd agree - that would probably break the 
current model I have proposed. *However*, providing a resource 
management layer as you have proposed above the basic resource 
management my API provides would resolve that without added complexity 
in the bridge drivers themselves.

> NB1. Any given driver knows nothing about the other devices
> on the crate; each driver only knows about the address, am and
> size of the mapping for the device it controls--user-space
> provide this info on a per-driver basis.
>   
Yes. I agree.
> NB2. The mapping offsets configured through the cards' pins match
> the information passed from user space to each of the cards.
>
>   
Yes. If I understand you correctly, your saying that management of the 
devices in the VME address space is a system configuration issue.

> Cheers,
> E.
>   
-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 15:10                   ` Martyn Welch
@ 2009-08-11 15:36                     ` Emilio G. Cota
  2009-08-11 15:41                       ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11 15:36 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Not the same question, but I'd agree - that would probably break the  
> current model I have proposed. *However*, providing a resource  
> management layer as you have proposed above the basic resource  
> management my API provides would resolve that without added complexity  
> in the bridge drivers themselves.

It wouldn't break it, the model simply couldn't give you more
than 8 windows-->8 devices.

I think it should be the bridge the one that manages its
own resources, not someone else.
I'm coding a layer that works this way, we'll see how it looks.

> Yes. If I understand you correctly, your saying that management of the  
> devices in the VME address space is a system configuration issue.

It obviously is. We cannot impose the users where they should
plug their devices or which pins on the boards they should
tweak. They build their crates --> they tell the kernel about
them.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 15:36                     ` Emilio G. Cota
@ 2009-08-11 15:41                       ` Martyn Welch
  2009-08-11 15:42                         ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11 15:41 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Not the same question, but I'd agree - that would probably break the  
>> current model I have proposed. *However*, providing a resource  
>> management layer as you have proposed above the basic resource  
>> management my API provides would resolve that without added complexity  
>> in the bridge drivers themselves.
>>     
>
> It wouldn't break it, the model simply couldn't give you more
> than 8 windows-->8 devices.
>   
Unless the devices we the same and the driver reused one window.
> I think it should be the bridge the one that manages its
> own resources, not someone else.
>   
I still think that layering this above the driver is better - it only 
needs to be written once rather than replicated for each bridge chip.
> I'm coding a layer that works this way, we'll see how it looks.
>
>   
Much obliged.
>> Yes. If I understand you correctly, your saying that management of the  
>> devices in the VME address space is a system configuration issue.
>>     
>
> It obviously is. We cannot impose the users where they should
> plug their devices or which pins on the boards they should
> tweak. They build their crates --> they tell the kernel about
> them.
>   
Agreed.

Martyn

-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 15:41                       ` Martyn Welch
@ 2009-08-11 15:42                         ` Emilio G. Cota
  2009-08-11 16:38                           ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11 15:42 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
>> It wouldn't break it, the model simply couldn't give you more
>> than 8 windows-->8 devices.
>>   
> Unless the devices we the same and the driver reused one window.

No, we agreed on that devices should know *nothing* about windows.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 15:42                         ` Emilio G. Cota
@ 2009-08-11 16:38                           ` Martyn Welch
  2009-08-11 20:48                             ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-11 16:38 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>>> It wouldn't break it, the model simply couldn't give you more
>>> than 8 windows-->8 devices.
>>>   
>>>       
>> Unless the devices we the same and the driver reused one window.
>>     
>
> No, we agreed on that devices should know *nothing* about windows.
>
>   
Don't think I did. :-)

I agreed that it would be impossible to assign sole use of 8 resources 
to 17 different drivers. I feel that there could be room and indeed a 
need for both approaches. It may be more efficient for a driver to use 
and move 1 window to position it's self over the registers of each of a 
number of compatible devices as required. Such a driver could exist 
side-by-side with drivers using your approach, that utilize a layer 
which removes such control. Whilst I understand you can assign a fairly 
sizable chunk of memory mapped IO for VME, this is not always the case. 
In situations where the available memory mapped IO is limited, placing a 
large window to cover 2 smaller windows with dead space in between them 
will not be an adequate solution either.

Martyn
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-11 16:38                           ` Martyn Welch
@ 2009-08-11 20:48                             ` Emilio G. Cota
       [not found]                               ` <4A828395.9040006@gefanuc.com>
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11 20:48 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> need for both approaches. It may be more efficient for a driver to use  
> and move 1 window to position it's self over the registers of each of a  
> number of compatible devices as required.

No, drivers should know nothing about the bridge. The *very* same
driver should run on different crates, different configurations,
different bridges.

The bridge should be intelligent enough to handle the driver's
mapping requests in an efficient way; it's basically the only
job of the bridge: to serve efficiently the driver's needs.
And, sorry to repeat myself, he's the only player entitled to
do so. "Because he knows better."

FWIW, our driver does exactly that. Will post it fairly soon,
but I want to test it with the new changes first.

Best wishes,
E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-11 12:46                     ` Martyn Welch
@ 2009-08-11 21:01                       ` Emilio G. Cota
  2009-08-12  8:17                         ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-11 21:01 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> I disagree. The bridge drivers should register their resources with the  
> core. The core, or a layer above it, can control how those resources are  
> used. This moves the complexity you want for managing the windows to a  
> level that will work on all underlying drivers rather than having to be  
> written explicitly for each one. The mechanism I have provided does this  
> discovery.

nah, it would be foolish to think we can write an upper layer
that covers every corner case for every bridge we're gonna
encounter. For instance, imagine a bridge that has 10 windows,
with the annoying feature that window#10 *only* accepts CS/CSR
mappings. How stupid is that? Very stupid. But what would be
more stupid is to write allegedly 'generic' interfaces that
break every time a bridge comes up with a stupid feature.

So that doesn't belong to a generic interface. Now, to avoid
code duplication between two (or more) _very_ similar bridges,
we just share the 'resource management' code among those,
privately. And that's pretty much it.

Regards,
E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-11 21:01                       ` Emilio G. Cota
@ 2009-08-12  8:17                         ` Martyn Welch
  2009-08-12  9:39                           ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-12  8:17 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> I disagree. The bridge drivers should register their resources with the  
>> core. The core, or a layer above it, can control how those resources are  
>> used. This moves the complexity you want for managing the windows to a  
>> level that will work on all underlying drivers rather than having to be  
>> written explicitly for each one. The mechanism I have provided does this  
>> discovery.
>>     
>
> nah, it would be foolish to think we can write an upper layer
> that covers every corner case for every bridge we're gonna
> encounter. 
So it's foolish to have a generic USB layer, or a generic PCI layer or 
generic "name you bus here" layer?

Unless you provide a consistent API, such as one supporting the features 
documented in the VME specifications, how are you planning to write 
drivers that could potentially work on more than one specific bridge?

> For instance, imagine a bridge that has 10 windows,
> with the annoying feature that window#10 *only* accepts CS/CSR
> mappings. How stupid is that? Very stupid. But what would be
> more stupid is to write allegedly 'generic' interfaces that
> break every time a bridge comes up with a stupid feature.
>   

Actually, the VME core as it exists would support such a situation. The 
10th window would only register as being capable of supporting that 
address space. What about a driver only needed to access CR/CSR space 
(to see what was available on other hosts for example), then utilized a 
DMA controller to transfer data over the VME bus  (thereby not requiring 
a master or slave window). It could request a window that fitted it's 
needs ("vme_address_t aspace = VMECRCST;") and the core would be able to 
hand it that resource. Should the underlying bridge not have such a 
limited window, it can had it one with a larger feature set, ensuring 
that it meets the requirements as requested by the driver.

> So that doesn't belong to a generic interface. Now, to avoid
> code duplication between two (or more) _very_ similar bridges,
> we just share the 'resource management' code among those,
> privately. And that's pretty much it.
>   

Or it could be layered on top, utilising the the resource management 
that I have proposed and the two can sit together happily side-by-side. 
If you are right and that method of access works best, then drivers will 
use that rather than requesting resources. If not then the two can 
continue to sit side-by-side. Why make the bridge drivers more complex 
than they need to be?

Also, it seems that your API doesn't currently support Location 
Monitors. These are specified in the VME spec, I'd be interested in how 
you plan to support this feature in a consistent manner with your 
current API. These can only be used in a single location at a time and 
cover a fixed number of addresses from the initial offset. The location 
monitors are treated as a resource in my VME core which is consistent 
with the windows being treated as resources.

Come to think of it, I can't see any code managing slave windows either 
- how is your API going to consistently manage these?

Martyn

> Regards,
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-12  8:17                         ` Martyn Welch
@ 2009-08-12  9:39                           ` Emilio G. Cota
  2009-08-12  9:57                             ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-12  9:39 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> So it's foolish to have a generic USB layer, or a generic PCI layer or  
> generic "name you bus here" layer?
[ snip ]
> Or it could be layered on top, utilising the the resource management
> that I have proposed and the two can sit together happily
> side-by-side.
> If you are right and that method of access works best, then drivers
> will
> use that rather than requesting resources. If not then the two can
> continue to sit side-by-side. Why make the bridge drivers more complex
> than they need to be?

aagh, please stop this nonsense.

http://lwn.net/Articles/336262/
"Here we see the first reason to dislike midlayers - they encourage
special cases. When writing a midlayer it is impossible to foresee every
possible need that a bottom level driver might have, so it is impossible
to allow for them all in the midlayer. The midlayer could conceivably be
redesigned every time a new requirement came along, but that is unlikely
to be an effective use of time. Instead, special cases tend to grow."

I won't discuss this anymore.

> Unless you provide a consistent API, such as one supporting the features  
> documented in the VME specifications, how are you planning to write  
> drivers that could potentially work on more than one specific bridge?
[ snip ]
> Also, it seems that your API doesn't currently support Location
> Monitors. These are specified in the VME spec, I'd be interested in
> how
> you plan to support this feature in a consistent manner with your
> current API

What I intend to provide is a sane environment to work with VME
devices. The first step is to cover the needs of most use cases:

- 1 master Single Board Computer (SBC) running Linux
- All the other slots have slave devices in them. These devices
  are controlled by kernel drivers on the SBC.
- The SBC does DMA, direct I/O access (mapping) to/from the
  slaves, and handles interrupts from the devices.

Once that's settled we could discuss about further parts of the
spec. But really today getting the above right is _much_ more
important than location monitors support.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
       [not found]                               ` <4A828395.9040006@gefanuc.com>
@ 2009-08-12  9:54                                 ` Emilio G. Cota
  2009-08-12  9:59                                   ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-12  9:54 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> There is a further device situated between the two.
> > How do you now protect the registers of this device from
> > > accidental/malicous/buggy behaviour from your board, since
> this region of the VME bus is now enabled for transfers
> in the PCI IO Mem space?

The kernel is not designed to work with buggy hardware--that
would be insane, we've got more than enough dealing with our
own bugs. However when there's no other solution (e.g.
hardware can't be fixed for whatever reason), fixes on a
per-case basis may be applied.

E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-12  9:39                           ` Emilio G. Cota
@ 2009-08-12  9:57                             ` Martyn Welch
  2009-08-12 11:20                               ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-12  9:57 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Also, it seems that your API doesn't currently support Location
>> Monitors. These are specified in the VME spec, I'd be interested in
>> how
>> you plan to support this feature in a consistent manner with your
>> current API
>>     
>
> What I intend to provide is a sane environment to work with VME
> devices. The first step is to cover the needs of most use cases:
>
> - 1 master Single Board Computer (SBC) running Linux
> - All the other slots have slave devices in them. These devices
>   are controlled by kernel drivers on the SBC.
> - The SBC does DMA, direct I/O access (mapping) to/from the
>   slaves, and handles interrupts from the devices.
>
> Once that's settled we could discuss about further parts of the
> spec. But really today getting the above right is _much_ more
> important than location monitors support.
>   
Right - this is a specific case that you see as important. Depending on 
the number of slots in the rack and the diversity of the cards used, the 
VME core and driver I have suggested already covers this.

However, I also need to consider other cases, such as communications 
between multiple SBCs and potentially a few slave devices. I have a need 
for this *now*, not in the future. To me, slave support and to a lesser 
degree location monitor support is important now, hence why it's 
supported in my driver and core. Hence why I am asking, how do you 
propose to support these features?

Martyn
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-12  9:54                                 ` Emilio G. Cota
@ 2009-08-12  9:59                                   ` Martyn Welch
  2009-08-12 10:09                                     ` Emilio G. Cota
  0 siblings, 1 reply; 65+ messages in thread
From: Martyn Welch @ 2009-08-12  9:59 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> There is a further device situated between the two.
>>     
>>> How do you now protect the registers of this device from
>>>       
>>>> accidental/malicous/buggy behaviour from your board, since
>>>>         
>> this region of the VME bus is now enabled for transfers
>> in the PCI IO Mem space?
>>     
>
> The kernel is not designed to work with buggy hardware--that
> would be insane, we've got more than enough dealing with our
> own bugs. However when there's no other solution (e.g.
> hardware can't be fixed for whatever reason), fixes on a
> per-case basis may be applied.
>
>   
Apart from : not mapping regions of the VME bus that you are not using.

Martyn

> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-12  9:59                                   ` Martyn Welch
@ 2009-08-12 10:09                                     ` Emilio G. Cota
  2009-08-12 10:19                                       ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-12 10:09 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> Apart from : not mapping regions of the VME bus that you are not using.

The tsi148 mappings have to be on 64k boundaries, so yes, it
happens that sometimes you map a bit more than what you need.
But there's no way to get around that; it's a hardware thing
so we just accept it.

E.

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

* Re: [patch 1/5] Staging: VME Framework for the Linux Kernel
  2009-08-12 10:09                                     ` Emilio G. Cota
@ 2009-08-12 10:19                                       ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-12 10:19 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>   
>> Apart from : not mapping regions of the VME bus that you are not using.
>>     
>
> The tsi148 mappings have to be on 64k boundaries, so yes, it
> happens that sometimes you map a bit more than what you need.
> But there's no way to get around that; it's a hardware thing
> so we just accept it.
>
>   

I agree - however you're talking about mapping areas which we don't need 
to map, to save the use of a window.

Martyn
> E.
>   


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)1327322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-12  9:57                             ` Martyn Welch
@ 2009-08-12 11:20                               ` Emilio G. Cota
  2009-08-12 12:19                                 ` Martyn Welch
  0 siblings, 1 reply; 65+ messages in thread
From: Emilio G. Cota @ 2009-08-12 11:20 UTC (permalink / raw)
  To: Martyn Welch; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Martyn Welch wrote:
> However, I also need to consider other cases, such as communications  
> between multiple SBCs and potentially a few slave devices. I have a need  
> for this *now*, not in the future. To me, slave support and to a lesser  
> degree location monitor support is important now, hence why it's  
> supported in my driver and core. Hence why I am asking, how do you  
> propose to support these features?

Point taken.

Could you tell me a bit more about your needs on this respect?
So, you have multiple SBCs (some of them as masters, some others
as slaves) running Linux and want to communicate between them?
Have you got working code for all that?

E.

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

* Re: [patch 2/5] Staging: vme: add VME userspace driver
  2009-08-12 11:20                               ` Emilio G. Cota
@ 2009-08-12 12:19                                 ` Martyn Welch
  0 siblings, 0 replies; 65+ messages in thread
From: Martyn Welch @ 2009-08-12 12:19 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Greg K-H, linux-kernel, devel, Sebastien Dugue

Emilio G. Cota wrote:
> Martyn Welch wrote:
>> However, I also need to consider other cases, such as communications  
>> between multiple SBCs and potentially a few slave devices. I have a need  
>> for this *now*, not in the future. To me, slave support and to a lesser  
>> degree location monitor support is important now, hence why it's  
>> supported in my driver and core. Hence why I am asking, how do you  
>> propose to support these features?
> 
> Point taken.
> 
> Could you tell me a bit more about your needs on this respect?
> So, you have multiple SBCs (some of them as masters, some others
> as slaves) running Linux and want to communicate between them?

Yes - though potentially each card will be both a master and a slave.

> Have you got working code for all that?

In so far as the VME core and tsi-148 driver supports the required functionality, I believe so. We have systems supporting this, though currently with different OSes. We also have a bit of legacy Linux VME code, using the old style of interface. At the moment my priority is less on individual applications and more focused towards ensuring we have a robust API which will support all the features we are likely to require for the chipsets that we have in our products.

We have briefly talked about a "Ethernet over VME" like the "Ethernet over RapidIO" driver, however, this currently isn't top priority.

Martyn

> 
> E.


-- 
Martyn Welch MEng MPhil MIET (Principal Software Engineer)   T:+44(0)127322748
GE Fanuc Intelligent Platforms Ltd,        |Registered in England and Wales
Tove Valley Business Park, Towcester,      |(3828642) at 100 Barbirolli Square,
Northants, NN12 6PF, UK T:+44(0)1327359444 |Manchester,M2 3AB  VAT:GB 927559189

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

end of thread, other threads:[~2009-08-12 12:19 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20090803205657.964064732@mini.kroah.org>
2009-08-03 21:00 ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Greg K-H
2009-08-03 21:01   ` [patch 1/5] Staging: VME Framework for the Linux Kernel Greg K-H
2009-08-08 23:01     ` Emilio G. Cota
2009-08-10 12:44       ` Martyn Welch
2009-08-10 14:14         ` Emilio G. Cota
2009-08-10 15:31           ` Emilio G. Cota
2009-08-10 16:40             ` Martyn Welch
2009-08-10 19:50               ` Emilio G. Cota
2009-08-11  8:02                 ` Martyn Welch
2009-08-11  8:43                   ` Emilio G. Cota
2009-08-10 15:53           ` Martyn Welch
2009-08-10 16:26             ` Shawn Bohrer
2009-08-10 19:38             ` Emilio G. Cota
2009-08-11  8:29               ` Martyn Welch
2009-08-11 14:49                 ` Emilio G. Cota
2009-08-11 15:10                   ` Martyn Welch
2009-08-11 15:36                     ` Emilio G. Cota
2009-08-11 15:41                       ` Martyn Welch
2009-08-11 15:42                         ` Emilio G. Cota
2009-08-11 16:38                           ` Martyn Welch
2009-08-11 20:48                             ` Emilio G. Cota
     [not found]                               ` <4A828395.9040006@gefanuc.com>
2009-08-12  9:54                                 ` Emilio G. Cota
2009-08-12  9:59                                   ` Martyn Welch
2009-08-12 10:09                                     ` Emilio G. Cota
2009-08-12 10:19                                       ` Martyn Welch
2009-08-11  4:54         ` Mike Frysinger
2009-08-11  7:48           ` Martyn Welch
2009-08-10 16:30       ` Greg KH
2009-08-10 14:52     ` [PATCH] Staging: vme: fix {master,slave}_get check bug Emilio G. Cota
2009-08-10 16:50       ` Martyn Welch
2009-08-03 21:01   ` [patch 2/5] Staging: vme: add VME userspace driver Greg K-H
2009-08-08 23:22     ` Emilio G. Cota
2009-08-09 12:17       ` Emilio G. Cota
2009-08-10 13:13         ` Martyn Welch
2009-08-10 15:26           ` Emilio G. Cota
2009-08-10 16:29             ` Greg KH
2009-08-10 16:30             ` Martyn Welch
2009-08-10 20:36               ` Emilio G. Cota
2009-08-11  9:03                 ` Martyn Welch
2009-08-11  9:40                   ` Emilio G. Cota
2009-08-11 12:46                     ` Martyn Welch
2009-08-11 21:01                       ` Emilio G. Cota
2009-08-12  8:17                         ` Martyn Welch
2009-08-12  9:39                           ` Emilio G. Cota
2009-08-12  9:57                             ` Martyn Welch
2009-08-12 11:20                               ` Emilio G. Cota
2009-08-12 12:19                                 ` Martyn Welch
2009-08-10 16:28         ` Greg KH
2009-08-10 20:05           ` Emilio G. Cota
2009-08-10 21:09             ` Greg KH
2009-08-11  7:04               ` Emilio G. Cota
2009-08-03 21:01   ` [patch 3/5] Staging: vme: add Universe I/II bridge driver Greg K-H
2009-08-03 23:00     ` Jiri Slaby
2009-08-03 21:01   ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Greg K-H
2009-08-03 22:50     ` Jiri Slaby
2009-08-03 22:55       ` Jiri Slaby
2009-08-05 16:33       ` [PATCH] Staging: Correct tsi-148 VME interrupt free routine Martyn Welch
2009-08-05 16:38         ` [PATCH v2] " Martyn Welch
2009-08-05 21:53           ` Jiri Slaby
2009-08-06  7:20             ` Martyn Welch
2009-08-09  0:09     ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridge driver Emilio G. Cota
2009-08-11 14:59       ` [patch 4/5] Staging: vme: add Tundra TSI148 VME-PCI Bridgedriver Martyn Welch
2009-08-03 21:01   ` [patch 5/5] Staging: vme: add TODO file Greg K-H
2009-08-04  7:56   ` [patch 0/5] [ANNOUNCE] VME Bus drivers and framework for Linux Martyn Welch
2009-08-08 22:25   ` Emilio G. Cota

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).