summaryrefslogtreecommitdiff
path: root/drivers/staging/unisys/visorutil/memregion_direct.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/unisys/visorutil/memregion_direct.c')
-rw-r--r--drivers/staging/unisys/visorutil/memregion_direct.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/staging/unisys/visorutil/memregion_direct.c b/drivers/staging/unisys/visorutil/memregion_direct.c
new file mode 100644
index 000000000..eb7422fbe
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion_direct.c
@@ -0,0 +1,207 @@
+/* memregion_direct.c
+ *
+ * Copyright (C) 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 "timskmod.h"
+#include "memregion.h"
+
+#define MYDRVNAME "memregion"
+
+struct memregion {
+ HOSTADDRESS physaddr;
+ ulong nbytes;
+ void __iomem *mapped;
+ BOOL requested;
+ BOOL overlapped;
+};
+
+static BOOL mapit(struct memregion *memregion);
+static void unmapit(struct memregion *memregion);
+
+struct memregion *
+visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes)
+{
+ struct memregion *rc = NULL;
+ struct memregion *memregion;
+
+ memregion = kzalloc(sizeof(*memregion), GFP_KERNEL | __GFP_NORETRY);
+ if (memregion == NULL)
+ return NULL;
+
+ memregion->physaddr = physaddr;
+ memregion->nbytes = nbytes;
+ memregion->overlapped = FALSE;
+ if (!mapit(memregion)) {
+ rc = NULL;
+ goto cleanup;
+ }
+ rc = memregion;
+cleanup:
+ if (rc == NULL) {
+ visor_memregion_destroy(memregion);
+ memregion = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_create);
+
+struct memregion *
+visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
+ ulong nbytes)
+{
+ struct memregion *memregion = NULL;
+
+ if (parent == NULL)
+ return NULL;
+
+ if (parent->mapped == NULL)
+ return NULL;
+
+ if ((offset >= parent->nbytes) ||
+ ((offset + nbytes) >= parent->nbytes))
+ return NULL;
+
+ memregion = kzalloc(sizeof(*memregion), GFP_KERNEL|__GFP_NORETRY);
+ if (memregion == NULL)
+ return NULL;
+
+ memregion->physaddr = parent->physaddr + offset;
+ memregion->nbytes = nbytes;
+ memregion->mapped = ((u8 __iomem *)(parent->mapped)) + offset;
+ memregion->requested = FALSE;
+ memregion->overlapped = TRUE;
+ return memregion;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
+
+static BOOL
+mapit(struct memregion *memregion)
+{
+ ulong physaddr = (ulong)(memregion->physaddr);
+ ulong nbytes = memregion->nbytes;
+
+ memregion->requested = FALSE;
+ if (request_mem_region(physaddr, nbytes, MYDRVNAME))
+ memregion->requested = TRUE;
+ memregion->mapped = ioremap_cache(physaddr, nbytes);
+ if (!memregion->mapped)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+unmapit(struct 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
+visor_memregion_get_physaddr(struct memregion *memregion)
+{
+ return memregion->physaddr;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
+
+ulong
+visor_memregion_get_nbytes(struct memregion *memregion)
+{
+ return memregion->nbytes;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
+
+void __iomem *
+visor_memregion_get_pointer(struct memregion *memregion)
+{
+ return memregion->mapped;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
+
+int
+visor_memregion_resize(struct 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(visor_memregion_resize);
+
+static int
+memregion_readwrite(BOOL is_write,
+ struct memregion *memregion, ulong offset,
+ void *local, ulong nbytes)
+{
+ if (offset + nbytes > memregion->nbytes)
+ return -EIO;
+
+ if (is_write)
+ memcpy_toio(memregion->mapped + offset, local, nbytes);
+ else
+ memcpy_fromio(local, memregion->mapped + offset, nbytes);
+
+ return 0;
+}
+
+int
+visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
+ ulong nbytes)
+{
+ return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_read);
+
+int
+visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
+ ulong nbytes)
+{
+ return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_write);
+
+void
+visor_memregion_destroy(struct memregion *memregion)
+{
+ if (memregion == NULL)
+ return;
+ if (!memregion->overlapped)
+ unmapit(memregion);
+ kfree(memregion);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_destroy);
+