blob: 33522cc8c22c9687d9c9fee70c764a7cfea5dcd1 [file] [log] [blame]
Ken Cox9d9baad2014-03-04 07:58:05 -06001/* memregion_direct.c
2 *
Benjamin Romerf6d0c1e2014-04-23 14:58:34 -04003 * Copyright (C) 2010 - 2013 UNISYS CORPORATION
Ken Cox9d9baad2014-03-04 07:58:05 -06004 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT. See the GNU General Public License for more
15 * details.
16 */
17
18/*
19 * This is an implementation of memory regions that can be used to read/write
20 * channel memory (in main memory of the host system) from code running in
21 * a virtual partition.
22 */
23#include "uniklog.h"
24#include "timskmod.h"
25#include "memregion.h"
26
27#define MYDRVNAME "memregion"
28
Benjamin Romerb63438c2014-11-04 11:25:15 -050029struct memregion {
Ken Cox9d9baad2014-03-04 07:58:05 -060030 HOSTADDRESS physaddr;
31 ulong nbytes;
Ken Cox3db55402014-05-22 12:31:09 -050032 void __iomem *mapped;
Ken Cox9d9baad2014-03-04 07:58:05 -060033 BOOL requested;
34 BOOL overlapped;
35};
36
Benjamin Romerb63438c2014-11-04 11:25:15 -050037static BOOL mapit(struct memregion *memregion);
38static void unmapit(struct memregion *memregion);
Ken Cox9d9baad2014-03-04 07:58:05 -060039
Benjamin Romerb63438c2014-11-04 11:25:15 -050040struct memregion *
Ken Cox927c7922014-03-05 14:52:25 -060041visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -060042{
Benjamin Romerb63438c2014-11-04 11:25:15 -050043 struct memregion *rc = NULL;
Benjamin Romerfef48382014-11-04 11:25:16 -050044 struct memregion *memregion = kzalloc(sizeof(*memregion),
45 GFP_KERNEL | __GFP_NORETRY);
Ken Cox9d9baad2014-03-04 07:58:05 -060046 if (memregion == NULL) {
Ken Cox927c7922014-03-05 14:52:25 -060047 ERRDRV("visor_memregion_create allocation failed");
Ken Cox9d9baad2014-03-04 07:58:05 -060048 return NULL;
49 }
Ken Cox9d9baad2014-03-04 07:58:05 -060050 memregion->physaddr = physaddr;
51 memregion->nbytes = nbytes;
52 memregion->overlapped = FALSE;
Ken Coxd9355f82014-03-19 13:06:22 -050053 if (!mapit(memregion)) {
54 rc = NULL;
Benjamin Romerfef48382014-11-04 11:25:16 -050055 goto cleanup;
Ken Coxd9355f82014-03-19 13:06:22 -050056 }
57 rc = memregion;
Benjamin Romerfef48382014-11-04 11:25:16 -050058cleanup:
Ken Cox9d9baad2014-03-04 07:58:05 -060059 if (rc == NULL) {
Sudip Mukherjeef9b64692014-11-07 17:48:34 +053060 visor_memregion_destroy(memregion);
61 memregion = NULL;
Ken Cox9d9baad2014-03-04 07:58:05 -060062 }
63 return rc;
64}
Ken Cox927c7922014-03-05 14:52:25 -060065EXPORT_SYMBOL_GPL(visor_memregion_create);
Ken Cox9d9baad2014-03-04 07:58:05 -060066
Benjamin Romerb63438c2014-11-04 11:25:15 -050067struct memregion *
68visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
69 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -060070{
Benjamin Romerb63438c2014-11-04 11:25:15 -050071 struct memregion *memregion = NULL;
Ken Cox9d9baad2014-03-04 07:58:05 -060072
73 if (parent == NULL) {
74 ERRDRV("%s parent is NULL", __func__);
75 return NULL;
76 }
77 if (parent->mapped == NULL) {
78 ERRDRV("%s parent is not mapped!", __func__);
79 return NULL;
80 }
81 if ((offset >= parent->nbytes) ||
82 ((offset + nbytes) >= parent->nbytes)) {
83 ERRDRV("%s range (%lu,%lu) out of parent range",
84 __func__, offset, nbytes);
85 return NULL;
86 }
Benjamin Romer6db05d32014-11-04 11:25:20 -050087 memregion = kzalloc(sizeof(*memregion), GFP_KERNEL|__GFP_NORETRY);
Ken Cox9d9baad2014-03-04 07:58:05 -060088 if (memregion == NULL) {
89 ERRDRV("%s allocation failed", __func__);
90 return NULL;
91 }
Andreea-Cristina Bernat97a84f12014-03-14 04:20:06 +020092
Ken Cox9d9baad2014-03-04 07:58:05 -060093 memregion->physaddr = parent->physaddr + offset;
94 memregion->nbytes = nbytes;
Benjamin Romer49695a532014-11-04 11:25:17 -050095 memregion->mapped = ((u8 __iomem *)(parent->mapped)) + offset;
Ken Cox9d9baad2014-03-04 07:58:05 -060096 memregion->requested = FALSE;
97 memregion->overlapped = TRUE;
98 return memregion;
99}
Ken Cox927c7922014-03-05 14:52:25 -0600100EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
Ken Cox9d9baad2014-03-04 07:58:05 -0600101
Ken Cox9d9baad2014-03-04 07:58:05 -0600102static BOOL
Benjamin Romerb63438c2014-11-04 11:25:15 -0500103mapit(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600104{
Benjamin Romer49695a532014-11-04 11:25:17 -0500105 ulong physaddr = (ulong)(memregion->physaddr);
Ken Cox9d9baad2014-03-04 07:58:05 -0600106 ulong nbytes = memregion->nbytes;
107
108 memregion->requested = FALSE;
109 if (!request_mem_region(physaddr, nbytes, MYDRVNAME))
Benjamin Romer92d00cf2014-11-04 11:25:19 -0500110 ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal",
111 physaddr, nbytes);
Ken Cox9d9baad2014-03-04 07:58:05 -0600112 else
113 memregion->requested = TRUE;
114 memregion->mapped = ioremap_cache(physaddr, nbytes);
115 if (memregion->mapped == NULL) {
116 ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx",
117 physaddr, nbytes);
118 return FALSE;
119 }
120 return TRUE;
121}
122
123static void
Benjamin Romerb63438c2014-11-04 11:25:15 -0500124unmapit(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600125{
126 if (memregion->mapped != NULL) {
127 iounmap(memregion->mapped);
128 memregion->mapped = NULL;
129 }
130 if (memregion->requested) {
Benjamin Romer49695a532014-11-04 11:25:17 -0500131 release_mem_region((ulong)(memregion->physaddr),
Ken Cox9d9baad2014-03-04 07:58:05 -0600132 memregion->nbytes);
133 memregion->requested = FALSE;
134 }
135}
136
137HOSTADDRESS
Benjamin Romerb63438c2014-11-04 11:25:15 -0500138visor_memregion_get_physaddr(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600139{
140 return memregion->physaddr;
141}
Ken Cox927c7922014-03-05 14:52:25 -0600142EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
Ken Cox9d9baad2014-03-04 07:58:05 -0600143
144ulong
Benjamin Romerb63438c2014-11-04 11:25:15 -0500145visor_memregion_get_nbytes(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600146{
147 return memregion->nbytes;
148}
Ken Cox927c7922014-03-05 14:52:25 -0600149EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
Ken Cox9d9baad2014-03-04 07:58:05 -0600150
Ken Cox3db55402014-05-22 12:31:09 -0500151void __iomem *
Benjamin Romerb63438c2014-11-04 11:25:15 -0500152visor_memregion_get_pointer(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600153{
154 return memregion->mapped;
155}
Ken Cox927c7922014-03-05 14:52:25 -0600156EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
Ken Cox9d9baad2014-03-04 07:58:05 -0600157
158int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500159visor_memregion_resize(struct memregion *memregion, ulong newsize)
Ken Cox9d9baad2014-03-04 07:58:05 -0600160{
161 if (newsize == memregion->nbytes)
162 return 0;
163 if (memregion->overlapped)
164 /* no error check here - we no longer know the
165 * parent's range!
166 */
167 memregion->nbytes = newsize;
168 else {
169 unmapit(memregion);
170 memregion->nbytes = newsize;
171 if (!mapit(memregion))
172 return -1;
173 }
174 return 0;
175}
Ken Cox927c7922014-03-05 14:52:25 -0600176EXPORT_SYMBOL_GPL(visor_memregion_resize);
Ken Cox9d9baad2014-03-04 07:58:05 -0600177
Ken Cox9d9baad2014-03-04 07:58:05 -0600178static int
179memregion_readwrite(BOOL is_write,
Benjamin Romerb63438c2014-11-04 11:25:15 -0500180 struct memregion *memregion, ulong offset,
Ken Cox9d9baad2014-03-04 07:58:05 -0600181 void *local, ulong nbytes)
182{
183 if (offset + nbytes > memregion->nbytes) {
184 ERRDRV("memregion_readwrite offset out of range!!");
Benjamin Romer66e24b72014-07-25 13:55:10 -0400185 return -EIO;
Ken Cox9d9baad2014-03-04 07:58:05 -0600186 }
187 if (is_write)
188 memcpy_toio(memregion->mapped + offset, local, nbytes);
189 else
190 memcpy_fromio(local, memregion->mapped + offset, nbytes);
191
192 return 0;
193}
194
195int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500196visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
Ken Cox927c7922014-03-05 14:52:25 -0600197 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -0600198{
199 return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
200}
Ken Cox927c7922014-03-05 14:52:25 -0600201EXPORT_SYMBOL_GPL(visor_memregion_read);
Ken Cox9d9baad2014-03-04 07:58:05 -0600202
203int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500204visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
Ken Cox927c7922014-03-05 14:52:25 -0600205 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -0600206{
207 return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
208}
Ken Cox927c7922014-03-05 14:52:25 -0600209EXPORT_SYMBOL_GPL(visor_memregion_write);
Ken Cox9d9baad2014-03-04 07:58:05 -0600210
211void
Benjamin Romerb63438c2014-11-04 11:25:15 -0500212visor_memregion_destroy(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600213{
214 if (memregion == NULL)
215 return;
216 if (!memregion->overlapped)
217 unmapit(memregion);
218 kfree(memregion);
219}
Ken Cox927c7922014-03-05 14:52:25 -0600220EXPORT_SYMBOL_GPL(visor_memregion_destroy);
Ken Cox9d9baad2014-03-04 07:58:05 -0600221