blob: 73228d2da30fc12e2c39551ed047e383e20f313c [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;
44 struct memregion *memregion = kzalloc(sizeof(struct memregion),
Silvio Fa3acc832014-03-18 22:13:10 +010045 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;
55 goto Away;
56 }
57 rc = memregion;
Ken Cox9d9baad2014-03-04 07:58:05 -060058Away:
59 if (rc == NULL) {
60 if (memregion != NULL) {
Ken Cox927c7922014-03-05 14:52:25 -060061 visor_memregion_destroy(memregion);
Ken Cox9d9baad2014-03-04 07:58:05 -060062 memregion = NULL;
63 }
64 }
65 return rc;
66}
Ken Cox927c7922014-03-05 14:52:25 -060067EXPORT_SYMBOL_GPL(visor_memregion_create);
Ken Cox9d9baad2014-03-04 07:58:05 -060068
Benjamin Romerb63438c2014-11-04 11:25:15 -050069struct memregion *
70visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
71 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -060072{
Benjamin Romerb63438c2014-11-04 11:25:15 -050073 struct memregion *memregion = NULL;
Ken Cox9d9baad2014-03-04 07:58:05 -060074
75 if (parent == NULL) {
76 ERRDRV("%s parent is NULL", __func__);
77 return NULL;
78 }
79 if (parent->mapped == NULL) {
80 ERRDRV("%s parent is not mapped!", __func__);
81 return NULL;
82 }
83 if ((offset >= parent->nbytes) ||
84 ((offset + nbytes) >= parent->nbytes)) {
85 ERRDRV("%s range (%lu,%lu) out of parent range",
86 __func__, offset, nbytes);
87 return NULL;
88 }
Benjamin Romerb63438c2014-11-04 11:25:15 -050089 memregion = kzalloc(sizeof(struct memregion), GFP_KERNEL|__GFP_NORETRY);
Ken Cox9d9baad2014-03-04 07:58:05 -060090 if (memregion == NULL) {
91 ERRDRV("%s allocation failed", __func__);
92 return NULL;
93 }
Andreea-Cristina Bernat97a84f12014-03-14 04:20:06 +020094
Ken Cox9d9baad2014-03-04 07:58:05 -060095 memregion->physaddr = parent->physaddr + offset;
96 memregion->nbytes = nbytes;
Ken Cox3db55402014-05-22 12:31:09 -050097 memregion->mapped = ((u8 __iomem *) (parent->mapped)) + offset;
Ken Cox9d9baad2014-03-04 07:58:05 -060098 memregion->requested = FALSE;
99 memregion->overlapped = TRUE;
100 return memregion;
101}
Ken Cox927c7922014-03-05 14:52:25 -0600102EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
Ken Cox9d9baad2014-03-04 07:58:05 -0600103
104
105static BOOL
Benjamin Romerb63438c2014-11-04 11:25:15 -0500106mapit(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600107{
108 ulong physaddr = (ulong) (memregion->physaddr);
109 ulong nbytes = memregion->nbytes;
110
111 memregion->requested = FALSE;
112 if (!request_mem_region(physaddr, nbytes, MYDRVNAME))
113 ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes);
114 else
115 memregion->requested = TRUE;
116 memregion->mapped = ioremap_cache(physaddr, nbytes);
117 if (memregion->mapped == NULL) {
118 ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx",
119 physaddr, nbytes);
120 return FALSE;
121 }
122 return TRUE;
123}
124
125static void
Benjamin Romerb63438c2014-11-04 11:25:15 -0500126unmapit(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600127{
128 if (memregion->mapped != NULL) {
129 iounmap(memregion->mapped);
130 memregion->mapped = NULL;
131 }
132 if (memregion->requested) {
133 release_mem_region((ulong) (memregion->physaddr),
134 memregion->nbytes);
135 memregion->requested = FALSE;
136 }
137}
138
139HOSTADDRESS
Benjamin Romerb63438c2014-11-04 11:25:15 -0500140visor_memregion_get_physaddr(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600141{
142 return memregion->physaddr;
143}
Ken Cox927c7922014-03-05 14:52:25 -0600144EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
Ken Cox9d9baad2014-03-04 07:58:05 -0600145
146ulong
Benjamin Romerb63438c2014-11-04 11:25:15 -0500147visor_memregion_get_nbytes(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600148{
149 return memregion->nbytes;
150}
Ken Cox927c7922014-03-05 14:52:25 -0600151EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
Ken Cox9d9baad2014-03-04 07:58:05 -0600152
Ken Cox3db55402014-05-22 12:31:09 -0500153void __iomem *
Benjamin Romerb63438c2014-11-04 11:25:15 -0500154visor_memregion_get_pointer(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600155{
156 return memregion->mapped;
157}
Ken Cox927c7922014-03-05 14:52:25 -0600158EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
Ken Cox9d9baad2014-03-04 07:58:05 -0600159
160int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500161visor_memregion_resize(struct memregion *memregion, ulong newsize)
Ken Cox9d9baad2014-03-04 07:58:05 -0600162{
163 if (newsize == memregion->nbytes)
164 return 0;
165 if (memregion->overlapped)
166 /* no error check here - we no longer know the
167 * parent's range!
168 */
169 memregion->nbytes = newsize;
170 else {
171 unmapit(memregion);
172 memregion->nbytes = newsize;
173 if (!mapit(memregion))
174 return -1;
175 }
176 return 0;
177}
Ken Cox927c7922014-03-05 14:52:25 -0600178EXPORT_SYMBOL_GPL(visor_memregion_resize);
Ken Cox9d9baad2014-03-04 07:58:05 -0600179
180
181static int
182memregion_readwrite(BOOL is_write,
Benjamin Romerb63438c2014-11-04 11:25:15 -0500183 struct memregion *memregion, ulong offset,
Ken Cox9d9baad2014-03-04 07:58:05 -0600184 void *local, ulong nbytes)
185{
186 if (offset + nbytes > memregion->nbytes) {
187 ERRDRV("memregion_readwrite offset out of range!!");
Benjamin Romer66e24b72014-07-25 13:55:10 -0400188 return -EIO;
Ken Cox9d9baad2014-03-04 07:58:05 -0600189 }
190 if (is_write)
191 memcpy_toio(memregion->mapped + offset, local, nbytes);
192 else
193 memcpy_fromio(local, memregion->mapped + offset, nbytes);
194
195 return 0;
196}
197
198int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500199visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
Ken Cox927c7922014-03-05 14:52:25 -0600200 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -0600201{
202 return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
203}
Ken Cox927c7922014-03-05 14:52:25 -0600204EXPORT_SYMBOL_GPL(visor_memregion_read);
Ken Cox9d9baad2014-03-04 07:58:05 -0600205
206int
Benjamin Romerb63438c2014-11-04 11:25:15 -0500207visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
Ken Cox927c7922014-03-05 14:52:25 -0600208 ulong nbytes)
Ken Cox9d9baad2014-03-04 07:58:05 -0600209{
210 return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
211}
Ken Cox927c7922014-03-05 14:52:25 -0600212EXPORT_SYMBOL_GPL(visor_memregion_write);
Ken Cox9d9baad2014-03-04 07:58:05 -0600213
214void
Benjamin Romerb63438c2014-11-04 11:25:15 -0500215visor_memregion_destroy(struct memregion *memregion)
Ken Cox9d9baad2014-03-04 07:58:05 -0600216{
217 if (memregion == NULL)
218 return;
219 if (!memregion->overlapped)
220 unmapit(memregion);
221 kfree(memregion);
222}
Ken Cox927c7922014-03-05 14:52:25 -0600223EXPORT_SYMBOL_GPL(visor_memregion_destroy);
Ken Cox9d9baad2014-03-04 07:58:05 -0600224