Commit | Line | Data |
---|---|---|
9d9baadd KC |
1 | /* memregion_direct.c |
2 | * | |
f6d0c1e6 | 3 | * Copyright (C) 2010 - 2013 UNISYS CORPORATION |
9d9baadd KC |
4 | * 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 | ||
29 | struct MEMREGION_Tag { | |
30 | HOSTADDRESS physaddr; | |
31 | ulong nbytes; | |
3db5540d | 32 | void __iomem *mapped; |
9d9baadd KC |
33 | BOOL requested; |
34 | BOOL overlapped; | |
35 | }; | |
36 | ||
37 | static BOOL mapit(MEMREGION *memregion); | |
38 | static void unmapit(MEMREGION *memregion); | |
39 | ||
40 | MEMREGION * | |
927c7927 | 41 | visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes) |
9d9baadd KC |
42 | { |
43 | MEMREGION *rc = NULL; | |
a3acc83a S |
44 | MEMREGION *memregion = kzalloc(sizeof(MEMREGION), |
45 | GFP_KERNEL | __GFP_NORETRY); | |
9d9baadd | 46 | if (memregion == NULL) { |
927c7927 | 47 | ERRDRV("visor_memregion_create allocation failed"); |
9d9baadd KC |
48 | return NULL; |
49 | } | |
9d9baadd KC |
50 | memregion->physaddr = physaddr; |
51 | memregion->nbytes = nbytes; | |
52 | memregion->overlapped = FALSE; | |
d9355f89 KC |
53 | if (!mapit(memregion)) { |
54 | rc = NULL; | |
55 | goto Away; | |
56 | } | |
57 | rc = memregion; | |
9d9baadd KC |
58 | Away: |
59 | if (rc == NULL) { | |
60 | if (memregion != NULL) { | |
927c7927 | 61 | visor_memregion_destroy(memregion); |
9d9baadd KC |
62 | memregion = NULL; |
63 | } | |
64 | } | |
65 | return rc; | |
66 | } | |
927c7927 | 67 | EXPORT_SYMBOL_GPL(visor_memregion_create); |
9d9baadd KC |
68 | |
69 | MEMREGION * | |
927c7927 | 70 | visor_memregion_create_overlapped(MEMREGION *parent, ulong offset, ulong nbytes) |
9d9baadd KC |
71 | { |
72 | MEMREGION *memregion = NULL; | |
73 | ||
74 | if (parent == NULL) { | |
75 | ERRDRV("%s parent is NULL", __func__); | |
76 | return NULL; | |
77 | } | |
78 | if (parent->mapped == NULL) { | |
79 | ERRDRV("%s parent is not mapped!", __func__); | |
80 | return NULL; | |
81 | } | |
82 | if ((offset >= parent->nbytes) || | |
83 | ((offset + nbytes) >= parent->nbytes)) { | |
84 | ERRDRV("%s range (%lu,%lu) out of parent range", | |
85 | __func__, offset, nbytes); | |
86 | return NULL; | |
87 | } | |
97a84f12 | 88 | memregion = kzalloc(sizeof(MEMREGION), GFP_KERNEL|__GFP_NORETRY); |
9d9baadd KC |
89 | if (memregion == NULL) { |
90 | ERRDRV("%s allocation failed", __func__); | |
91 | return NULL; | |
92 | } | |
97a84f12 | 93 | |
9d9baadd KC |
94 | memregion->physaddr = parent->physaddr + offset; |
95 | memregion->nbytes = nbytes; | |
3db5540d | 96 | memregion->mapped = ((u8 __iomem *) (parent->mapped)) + offset; |
9d9baadd KC |
97 | memregion->requested = FALSE; |
98 | memregion->overlapped = TRUE; | |
99 | return memregion; | |
100 | } | |
927c7927 | 101 | EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped); |
9d9baadd KC |
102 | |
103 | ||
104 | static BOOL | |
105 | mapit(MEMREGION *memregion) | |
106 | { | |
107 | ulong physaddr = (ulong) (memregion->physaddr); | |
108 | ulong nbytes = memregion->nbytes; | |
109 | ||
110 | memregion->requested = FALSE; | |
111 | if (!request_mem_region(physaddr, nbytes, MYDRVNAME)) | |
112 | ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes); | |
113 | else | |
114 | memregion->requested = TRUE; | |
115 | memregion->mapped = ioremap_cache(physaddr, nbytes); | |
116 | if (memregion->mapped == NULL) { | |
117 | ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx", | |
118 | physaddr, nbytes); | |
119 | return FALSE; | |
120 | } | |
121 | return TRUE; | |
122 | } | |
123 | ||
124 | static void | |
125 | unmapit(MEMREGION *memregion) | |
126 | { | |
127 | if (memregion->mapped != NULL) { | |
128 | iounmap(memregion->mapped); | |
129 | memregion->mapped = NULL; | |
130 | } | |
131 | if (memregion->requested) { | |
132 | release_mem_region((ulong) (memregion->physaddr), | |
133 | memregion->nbytes); | |
134 | memregion->requested = FALSE; | |
135 | } | |
136 | } | |
137 | ||
138 | HOSTADDRESS | |
927c7927 | 139 | visor_memregion_get_physaddr(MEMREGION *memregion) |
9d9baadd KC |
140 | { |
141 | return memregion->physaddr; | |
142 | } | |
927c7927 | 143 | EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr); |
9d9baadd KC |
144 | |
145 | ulong | |
927c7927 | 146 | visor_memregion_get_nbytes(MEMREGION *memregion) |
9d9baadd KC |
147 | { |
148 | return memregion->nbytes; | |
149 | } | |
927c7927 | 150 | EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes); |
9d9baadd | 151 | |
3db5540d | 152 | void __iomem * |
927c7927 | 153 | visor_memregion_get_pointer(MEMREGION *memregion) |
9d9baadd KC |
154 | { |
155 | return memregion->mapped; | |
156 | } | |
927c7927 | 157 | EXPORT_SYMBOL_GPL(visor_memregion_get_pointer); |
9d9baadd KC |
158 | |
159 | int | |
927c7927 | 160 | visor_memregion_resize(MEMREGION *memregion, ulong newsize) |
9d9baadd KC |
161 | { |
162 | if (newsize == memregion->nbytes) | |
163 | return 0; | |
164 | if (memregion->overlapped) | |
165 | /* no error check here - we no longer know the | |
166 | * parent's range! | |
167 | */ | |
168 | memregion->nbytes = newsize; | |
169 | else { | |
170 | unmapit(memregion); | |
171 | memregion->nbytes = newsize; | |
172 | if (!mapit(memregion)) | |
173 | return -1; | |
174 | } | |
175 | return 0; | |
176 | } | |
927c7927 | 177 | EXPORT_SYMBOL_GPL(visor_memregion_resize); |
9d9baadd KC |
178 | |
179 | ||
180 | static int | |
181 | memregion_readwrite(BOOL is_write, | |
182 | MEMREGION *memregion, ulong offset, | |
183 | void *local, ulong nbytes) | |
184 | { | |
185 | if (offset + nbytes > memregion->nbytes) { | |
186 | ERRDRV("memregion_readwrite offset out of range!!"); | |
66e24b76 | 187 | return -EIO; |
9d9baadd KC |
188 | } |
189 | if (is_write) | |
190 | memcpy_toio(memregion->mapped + offset, local, nbytes); | |
191 | else | |
192 | memcpy_fromio(local, memregion->mapped + offset, nbytes); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | int | |
927c7927 KC |
198 | visor_memregion_read(MEMREGION *memregion, ulong offset, void *dest, |
199 | ulong nbytes) | |
9d9baadd KC |
200 | { |
201 | return memregion_readwrite(FALSE, memregion, offset, dest, nbytes); | |
202 | } | |
927c7927 | 203 | EXPORT_SYMBOL_GPL(visor_memregion_read); |
9d9baadd KC |
204 | |
205 | int | |
927c7927 KC |
206 | visor_memregion_write(MEMREGION *memregion, ulong offset, void *src, |
207 | ulong nbytes) | |
9d9baadd KC |
208 | { |
209 | return memregion_readwrite(TRUE, memregion, offset, src, nbytes); | |
210 | } | |
927c7927 | 211 | EXPORT_SYMBOL_GPL(visor_memregion_write); |
9d9baadd KC |
212 | |
213 | void | |
927c7927 | 214 | visor_memregion_destroy(MEMREGION *memregion) |
9d9baadd KC |
215 | { |
216 | if (memregion == NULL) | |
217 | return; | |
218 | if (!memregion->overlapped) | |
219 | unmapit(memregion); | |
220 | kfree(memregion); | |
221 | } | |
927c7927 | 222 | EXPORT_SYMBOL_GPL(visor_memregion_destroy); |
9d9baadd | 223 |