blob: 79e110d4a81a141a3734e82c14b482b2c5d453e9 [file] [log] [blame]
Dan Williams6bc75612015-06-17 17:23:32 -04001/*
2 * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 */
13#include <linux/rculist.h>
14#include <linux/export.h>
15#include <linux/ioport.h>
16#include <linux/module.h>
17#include <linux/types.h>
18#include <linux/io.h>
19#include "nfit_test.h"
20
21static LIST_HEAD(iomap_head);
22
23static struct iomap_ops {
24 nfit_test_lookup_fn nfit_test_lookup;
25 struct list_head list;
26} iomap_ops = {
27 .list = LIST_HEAD_INIT(iomap_ops.list),
28};
29
30void nfit_test_setup(nfit_test_lookup_fn lookup)
31{
32 iomap_ops.nfit_test_lookup = lookup;
33 list_add_rcu(&iomap_ops.list, &iomap_head);
34}
35EXPORT_SYMBOL(nfit_test_setup);
36
37void nfit_test_teardown(void)
38{
39 list_del_rcu(&iomap_ops.list);
40 synchronize_rcu();
41}
42EXPORT_SYMBOL(nfit_test_teardown);
43
44static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
45{
46 struct iomap_ops *ops;
47
48 ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
49 if (ops)
50 return ops->nfit_test_lookup(resource);
51 return NULL;
52}
53
54void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
55 void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
56{
57 struct nfit_test_resource *nfit_res;
58
59 rcu_read_lock();
60 nfit_res = get_nfit_res(offset);
61 rcu_read_unlock();
62 if (nfit_res)
63 return (void __iomem *) nfit_res->buf + offset
64 - nfit_res->res->start;
65 return fallback_fn(offset, size);
66}
67
Dan Williams9d27a872015-07-10 14:07:03 -040068void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
69 resource_size_t offset, unsigned long size)
70{
71 struct nfit_test_resource *nfit_res;
72
73 rcu_read_lock();
74 nfit_res = get_nfit_res(offset);
75 rcu_read_unlock();
76 if (nfit_res)
77 return (void __iomem *) nfit_res->buf + offset
78 - nfit_res->res->start;
79 return devm_ioremap_nocache(dev, offset, size);
80}
81EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
82
Christoph Hellwig708ab622015-08-10 23:07:08 -040083void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
84 size_t size, unsigned long flags)
Dan Williams6bc75612015-06-17 17:23:32 -040085{
Dan Williamse836a252015-08-12 18:42:56 -040086 struct nfit_test_resource *nfit_res;
87
88 rcu_read_lock();
89 nfit_res = get_nfit_res(offset);
90 rcu_read_unlock();
91 if (nfit_res)
Ross Zwisler67a3e8f2015-08-27 13:14:20 -060092 return nfit_res->buf + offset - nfit_res->res->start;
Christoph Hellwig708ab622015-08-10 23:07:08 -040093 return devm_memremap(dev, offset, size, flags);
Dan Williams6bc75612015-06-17 17:23:32 -040094}
Christoph Hellwig708ab622015-08-10 23:07:08 -040095EXPORT_SYMBOL(__wrap_devm_memremap);
Dan Williams6bc75612015-06-17 17:23:32 -040096
Dan Williams979fccf2015-12-15 00:34:21 -080097#ifdef __HAVE_ARCH_PTE_DEVMAP
98#include <linux/memremap.h>
99#include <linux/pfn_t.h>
100
101void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
102 struct percpu_ref *ref, struct vmem_altmap *altmap)
103{
104 resource_size_t offset = res->start;
105 struct nfit_test_resource *nfit_res;
106
107 rcu_read_lock();
108 nfit_res = get_nfit_res(offset);
109 rcu_read_unlock();
110 if (nfit_res)
111 return nfit_res->buf + offset - nfit_res->res->start;
112 return devm_memremap_pages(dev, res, ref, altmap);
113}
114EXPORT_SYMBOL(__wrap_devm_memremap_pages);
115
116pfn_t __wrap_phys_to_pfn_t(dma_addr_t addr, unsigned long flags)
117{
118 struct nfit_test_resource *nfit_res;
119
120 rcu_read_lock();
121 nfit_res = get_nfit_res(addr);
122 rcu_read_unlock();
123 if (nfit_res)
124 flags &= ~PFN_MAP;
125 return phys_to_pfn_t(addr, flags);
126}
127EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
128#else
129/* to be removed post 4.5-rc1 */
130void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res)
131{
132 resource_size_t offset = res->start;
133 struct nfit_test_resource *nfit_res;
134
135 rcu_read_lock();
136 nfit_res = get_nfit_res(offset);
137 rcu_read_unlock();
138 if (nfit_res)
139 return nfit_res->buf + offset - nfit_res->res->start;
140 return devm_memremap_pages(dev, res);
141}
142EXPORT_SYMBOL(__wrap_devm_memremap_pages);
143#endif
144
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600145void *__wrap_memremap(resource_size_t offset, size_t size,
146 unsigned long flags)
147{
148 struct nfit_test_resource *nfit_res;
149
150 rcu_read_lock();
151 nfit_res = get_nfit_res(offset);
152 rcu_read_unlock();
153 if (nfit_res)
154 return nfit_res->buf + offset - nfit_res->res->start;
155 return memremap(offset, size, flags);
156}
157EXPORT_SYMBOL(__wrap_memremap);
158
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400159void __wrap_devm_memunmap(struct device *dev, void *addr)
160{
161 struct nfit_test_resource *nfit_res;
162
163 rcu_read_lock();
164 nfit_res = get_nfit_res((unsigned long) addr);
165 rcu_read_unlock();
166 if (nfit_res)
167 return;
168 return devm_memunmap(dev, addr);
169}
170EXPORT_SYMBOL(__wrap_devm_memunmap);
171
Dan Williams6bc75612015-06-17 17:23:32 -0400172void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
173{
174 return __nfit_test_ioremap(offset, size, ioremap_nocache);
175}
176EXPORT_SYMBOL(__wrap_ioremap_nocache);
177
Dan Williams9d27a872015-07-10 14:07:03 -0400178void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
179{
180 return __nfit_test_ioremap(offset, size, ioremap_wc);
181}
182EXPORT_SYMBOL(__wrap_ioremap_wc);
183
Dan Williams6bc75612015-06-17 17:23:32 -0400184void __wrap_iounmap(volatile void __iomem *addr)
185{
186 struct nfit_test_resource *nfit_res;
187
188 rcu_read_lock();
189 nfit_res = get_nfit_res((unsigned long) addr);
190 rcu_read_unlock();
191 if (nfit_res)
192 return;
193 return iounmap(addr);
194}
195EXPORT_SYMBOL(__wrap_iounmap);
196
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600197void __wrap_memunmap(void *addr)
198{
199 struct nfit_test_resource *nfit_res;
200
201 rcu_read_lock();
202 nfit_res = get_nfit_res((unsigned long) addr);
203 rcu_read_unlock();
204 if (nfit_res)
205 return;
206 return memunmap(addr);
207}
208EXPORT_SYMBOL(__wrap_memunmap);
209
Christoph Hellwig708ab622015-08-10 23:07:08 -0400210static struct resource *nfit_test_request_region(struct device *dev,
211 struct resource *parent, resource_size_t start,
212 resource_size_t n, const char *name, int flags)
Dan Williams6bc75612015-06-17 17:23:32 -0400213{
214 struct nfit_test_resource *nfit_res;
215
216 if (parent == &iomem_resource) {
217 rcu_read_lock();
218 nfit_res = get_nfit_res(start);
219 rcu_read_unlock();
220 if (nfit_res) {
221 struct resource *res = nfit_res->res + 1;
222
223 if (start + n > nfit_res->res->start
224 + resource_size(nfit_res->res)) {
225 pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
226 __func__, start, n,
227 nfit_res->res);
228 return NULL;
229 }
230
231 res->start = start;
232 res->end = start + n - 1;
233 res->name = name;
234 res->flags = resource_type(parent);
235 res->flags |= IORESOURCE_BUSY | flags;
236 pr_debug("%s: %pr\n", __func__, res);
237 return res;
238 }
239 }
Christoph Hellwig708ab622015-08-10 23:07:08 -0400240 if (dev)
241 return __devm_request_region(dev, parent, start, n, name);
Dan Williams6bc75612015-06-17 17:23:32 -0400242 return __request_region(parent, start, n, name, flags);
243}
Christoph Hellwig708ab622015-08-10 23:07:08 -0400244
245struct resource *__wrap___request_region(struct resource *parent,
246 resource_size_t start, resource_size_t n, const char *name,
247 int flags)
248{
249 return nfit_test_request_region(NULL, parent, start, n, name, flags);
250}
Dan Williams6bc75612015-06-17 17:23:32 -0400251EXPORT_SYMBOL(__wrap___request_region);
252
Christoph Hellwig708ab622015-08-10 23:07:08 -0400253struct resource *__wrap___devm_request_region(struct device *dev,
254 struct resource *parent, resource_size_t start,
255 resource_size_t n, const char *name)
256{
257 if (!dev)
258 return NULL;
259 return nfit_test_request_region(dev, parent, start, n, name, 0);
260}
261EXPORT_SYMBOL(__wrap___devm_request_region);
262
Dan Williams6bc75612015-06-17 17:23:32 -0400263void __wrap___release_region(struct resource *parent, resource_size_t start,
264 resource_size_t n)
265{
266 struct nfit_test_resource *nfit_res;
267
268 if (parent == &iomem_resource) {
269 rcu_read_lock();
270 nfit_res = get_nfit_res(start);
271 rcu_read_unlock();
272 if (nfit_res) {
273 struct resource *res = nfit_res->res + 1;
274
275 if (start != res->start || resource_size(res) != n)
276 pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
277 __func__, start, n, res);
278 else
279 memset(res, 0, sizeof(*res));
280 return;
281 }
282 }
283 __release_region(parent, start, n);
284}
285EXPORT_SYMBOL(__wrap___release_region);
286
287MODULE_LICENSE("GPL v2");