staging: visorutil driver to provide common functionality to other s-Par drivers

The visorutil module is a support library required by all other s-Par
driver modules. Among its features it abstracts reading, writing, and
manipulating a block of memory.

Signed-off-by: Ken Cox <jkc@redhat.com>
Cc: Ben Romer <sparmaintainer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/staging/unisys/visorutil/memregion_direct.c b/drivers/staging/unisys/visorutil/memregion_direct.c
new file mode 100644
index 0000000..fbb460f
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion_direct.c
@@ -0,0 +1,221 @@
+/* memregion_direct.c
+ *
+ * Copyright © 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ *  This is an implementation of memory regions that can be used to read/write
+ *  channel memory (in main memory of the host system) from code running in
+ *  a virtual partition.
+ */
+#include "uniklog.h"
+#include "timskmod.h"
+#include "memregion.h"
+
+#define MYDRVNAME "memregion"
+
+struct MEMREGION_Tag {
+	HOSTADDRESS physaddr;
+	ulong nbytes;
+	void *mapped;
+	BOOL requested;
+	BOOL overlapped;
+};
+
+static BOOL mapit(MEMREGION *memregion);
+static void unmapit(MEMREGION *memregion);
+
+MEMREGION *
+memregion_create(HOSTADDRESS physaddr, ulong nbytes)
+{
+	MEMREGION *rc = NULL;
+	MEMREGION *memregion = kmalloc(sizeof(MEMREGION),
+				       GFP_KERNEL|__GFP_NORETRY);
+	if (memregion == NULL) {
+		ERRDRV("memregion_create allocation failed");
+		return NULL;
+	}
+	memset(memregion, 0, sizeof(MEMREGION));
+	memregion->physaddr = physaddr;
+	memregion->nbytes = nbytes;
+	memregion->overlapped = FALSE;
+	if (!mapit(memregion))
+		RETPTR(NULL);
+	RETPTR(memregion);
+
+Away:
+	if (rc == NULL) {
+		if (memregion != NULL) {
+			memregion_destroy(memregion);
+			memregion = NULL;
+		}
+	}
+	return rc;
+}
+EXPORT_SYMBOL_GPL(memregion_create);
+
+MEMREGION *
+memregion_create_overlapped(MEMREGION *parent, ulong offset, ulong nbytes)
+{
+	MEMREGION *memregion = NULL;
+
+	if (parent == NULL) {
+		ERRDRV("%s parent is NULL", __func__);
+		return NULL;
+	}
+	if (parent->mapped == NULL) {
+		ERRDRV("%s parent is not mapped!", __func__);
+		return NULL;
+	}
+	if ((offset >= parent->nbytes) ||
+	    ((offset + nbytes) >= parent->nbytes)) {
+		ERRDRV("%s range (%lu,%lu) out of parent range",
+		       __func__, offset, nbytes);
+		return NULL;
+	}
+	memregion = kmalloc(sizeof(MEMREGION), GFP_KERNEL|__GFP_NORETRY);
+	if (memregion == NULL) {
+		ERRDRV("%s allocation failed", __func__);
+		return NULL;
+	}
+	memset(memregion, 0, sizeof(MEMREGION));
+	memregion->physaddr = parent->physaddr + offset;
+	memregion->nbytes = nbytes;
+	memregion->mapped = ((u8 *) (parent->mapped)) + offset;
+	memregion->requested = FALSE;
+	memregion->overlapped = TRUE;
+	return memregion;
+}
+EXPORT_SYMBOL_GPL(memregion_create_overlapped);
+
+
+static BOOL
+mapit(MEMREGION *memregion)
+{
+	ulong physaddr = (ulong) (memregion->physaddr);
+	ulong nbytes = memregion->nbytes;
+
+	memregion->requested = FALSE;
+	if (!request_mem_region(physaddr, nbytes, MYDRVNAME))
+		ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes);
+	else
+		memregion->requested = TRUE;
+	memregion->mapped = ioremap_cache(physaddr, nbytes);
+	if (memregion->mapped == NULL) {
+		ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx",
+		       physaddr, nbytes);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+unmapit(MEMREGION *memregion)
+{
+	if (memregion->mapped != NULL) {
+		iounmap(memregion->mapped);
+		memregion->mapped = NULL;
+	}
+	if (memregion->requested) {
+		release_mem_region((ulong) (memregion->physaddr),
+				   memregion->nbytes);
+		memregion->requested = FALSE;
+	}
+}
+
+HOSTADDRESS
+memregion_get_physaddr(MEMREGION *memregion)
+{
+	return memregion->physaddr;
+}
+EXPORT_SYMBOL_GPL(memregion_get_physaddr);
+
+ulong
+memregion_get_nbytes(MEMREGION *memregion)
+{
+	return memregion->nbytes;
+}
+EXPORT_SYMBOL_GPL(memregion_get_nbytes);
+
+void *
+memregion_get_pointer(MEMREGION *memregion)
+{
+	return memregion->mapped;
+}
+EXPORT_SYMBOL_GPL(memregion_get_pointer);
+
+int
+memregion_resize(MEMREGION *memregion, ulong newsize)
+{
+	if (newsize == memregion->nbytes)
+		return 0;
+	if (memregion->overlapped)
+		/* no error check here - we no longer know the
+		 * parent's range!
+		 */
+		memregion->nbytes = newsize;
+	else {
+		unmapit(memregion);
+		memregion->nbytes = newsize;
+		if (!mapit(memregion))
+			return -1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(memregion_resize);
+
+
+static int
+memregion_readwrite(BOOL is_write,
+		    MEMREGION *memregion, ulong offset,
+		    void *local, ulong nbytes)
+{
+	if (offset + nbytes > memregion->nbytes) {
+		ERRDRV("memregion_readwrite offset out of range!!");
+		return -EFAULT;
+	}
+	if (is_write)
+		memcpy_toio(memregion->mapped + offset, local, nbytes);
+	else
+		memcpy_fromio(local, memregion->mapped + offset, nbytes);
+
+	return 0;
+}
+
+int
+memregion_read(MEMREGION *memregion, ulong offset, void *dest, ulong nbytes)
+{
+	return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
+}
+EXPORT_SYMBOL_GPL(memregion_read);
+
+int
+memregion_write(MEMREGION *memregion, ulong offset, void *src, ulong nbytes)
+{
+	return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
+}
+EXPORT_SYMBOL_GPL(memregion_write);
+
+void
+memregion_destroy(MEMREGION *memregion)
+{
+	if (memregion == NULL)
+		return;
+	if (!memregion->overlapped)
+		unmapit(memregion);
+	kfree(memregion);
+}
+EXPORT_SYMBOL_GPL(memregion_destroy);
+