blob: 0c1a7e65bb815a692f9b20cd97b337c181699308 [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>
Dan Williams9bfa8492015-12-11 11:20:16 -080019#include <linux/mm.h>
Dan Williams6bc75612015-06-17 17:23:32 -040020#include "nfit_test.h"
21
22static LIST_HEAD(iomap_head);
23
24static struct iomap_ops {
25 nfit_test_lookup_fn nfit_test_lookup;
26 struct list_head list;
27} iomap_ops = {
28 .list = LIST_HEAD_INIT(iomap_ops.list),
29};
30
31void nfit_test_setup(nfit_test_lookup_fn lookup)
32{
33 iomap_ops.nfit_test_lookup = lookup;
34 list_add_rcu(&iomap_ops.list, &iomap_head);
35}
36EXPORT_SYMBOL(nfit_test_setup);
37
38void nfit_test_teardown(void)
39{
40 list_del_rcu(&iomap_ops.list);
41 synchronize_rcu();
42}
43EXPORT_SYMBOL(nfit_test_teardown);
44
Dan Williams9bfa8492015-12-11 11:20:16 -080045static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
Dan Williams6bc75612015-06-17 17:23:32 -040046{
47 struct iomap_ops *ops;
48
49 ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list);
50 if (ops)
51 return ops->nfit_test_lookup(resource);
52 return NULL;
53}
54
Dan Williams9bfa8492015-12-11 11:20:16 -080055static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
56{
57 struct nfit_test_resource *res;
58
59 rcu_read_lock();
60 res = __get_nfit_res(resource);
61 rcu_read_unlock();
62
63 return res;
64}
65
Dan Williams6bc75612015-06-17 17:23:32 -040066void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
67 void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
68{
Dan Williams9bfa8492015-12-11 11:20:16 -080069 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams6bc75612015-06-17 17:23:32 -040070
Dan Williams6bc75612015-06-17 17:23:32 -040071 if (nfit_res)
72 return (void __iomem *) nfit_res->buf + offset
73 - nfit_res->res->start;
74 return fallback_fn(offset, size);
75}
76
Dan Williams9d27a872015-07-10 14:07:03 -040077void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
78 resource_size_t offset, unsigned long size)
79{
Dan Williams9bfa8492015-12-11 11:20:16 -080080 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams9d27a872015-07-10 14:07:03 -040081
Dan Williams9d27a872015-07-10 14:07:03 -040082 if (nfit_res)
83 return (void __iomem *) nfit_res->buf + offset
84 - nfit_res->res->start;
85 return devm_ioremap_nocache(dev, offset, size);
86}
87EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
88
Christoph Hellwig708ab622015-08-10 23:07:08 -040089void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
90 size_t size, unsigned long flags)
Dan Williams6bc75612015-06-17 17:23:32 -040091{
Dan Williams9bfa8492015-12-11 11:20:16 -080092 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williamse836a252015-08-12 18:42:56 -040093
Dan Williamse836a252015-08-12 18:42:56 -040094 if (nfit_res)
Ross Zwisler67a3e8f2015-08-27 13:14:20 -060095 return nfit_res->buf + offset - nfit_res->res->start;
Christoph Hellwig708ab622015-08-10 23:07:08 -040096 return devm_memremap(dev, offset, size, flags);
Dan Williams6bc75612015-06-17 17:23:32 -040097}
Christoph Hellwig708ab622015-08-10 23:07:08 -040098EXPORT_SYMBOL(__wrap_devm_memremap);
Dan Williams6bc75612015-06-17 17:23:32 -040099
Dan Williams979fccf2015-12-15 00:34:21 -0800100#ifdef __HAVE_ARCH_PTE_DEVMAP
101#include <linux/memremap.h>
102#include <linux/pfn_t.h>
103
104void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
105 struct percpu_ref *ref, struct vmem_altmap *altmap)
106{
107 resource_size_t offset = res->start;
Dan Williams9bfa8492015-12-11 11:20:16 -0800108 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams979fccf2015-12-15 00:34:21 -0800109
Dan Williams979fccf2015-12-15 00:34:21 -0800110 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
Dan Williams76e9f0e2016-01-22 09:43:28 -0800116pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags)
Dan Williams979fccf2015-12-15 00:34:21 -0800117{
Dan Williams9bfa8492015-12-11 11:20:16 -0800118 struct nfit_test_resource *nfit_res = get_nfit_res(addr);
Dan Williams979fccf2015-12-15 00:34:21 -0800119
Dan Williams979fccf2015-12-15 00:34:21 -0800120 if (nfit_res)
121 flags &= ~PFN_MAP;
122 return phys_to_pfn_t(addr, flags);
123}
124EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
125#else
126/* to be removed post 4.5-rc1 */
127void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res)
128{
129 resource_size_t offset = res->start;
Dan Williams9bfa8492015-12-11 11:20:16 -0800130 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Dan Williams979fccf2015-12-15 00:34:21 -0800131
Dan Williams979fccf2015-12-15 00:34:21 -0800132 if (nfit_res)
133 return nfit_res->buf + offset - nfit_res->res->start;
134 return devm_memremap_pages(dev, res);
135}
136EXPORT_SYMBOL(__wrap_devm_memremap_pages);
137#endif
138
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600139void *__wrap_memremap(resource_size_t offset, size_t size,
140 unsigned long flags)
141{
Dan Williams9bfa8492015-12-11 11:20:16 -0800142 struct nfit_test_resource *nfit_res = get_nfit_res(offset);
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600143
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600144 if (nfit_res)
145 return nfit_res->buf + offset - nfit_res->res->start;
146 return memremap(offset, size, flags);
147}
148EXPORT_SYMBOL(__wrap_memremap);
149
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400150void __wrap_devm_memunmap(struct device *dev, void *addr)
151{
Dan Williams9bfa8492015-12-11 11:20:16 -0800152 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400153
Dan Williams32ab0a3f2015-08-01 02:16:37 -0400154 if (nfit_res)
155 return;
156 return devm_memunmap(dev, addr);
157}
158EXPORT_SYMBOL(__wrap_devm_memunmap);
159
Dan Williams6bc75612015-06-17 17:23:32 -0400160void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size)
161{
162 return __nfit_test_ioremap(offset, size, ioremap_nocache);
163}
164EXPORT_SYMBOL(__wrap_ioremap_nocache);
165
Dan Williams9d27a872015-07-10 14:07:03 -0400166void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size)
167{
168 return __nfit_test_ioremap(offset, size, ioremap_wc);
169}
170EXPORT_SYMBOL(__wrap_ioremap_wc);
171
Dan Williams6bc75612015-06-17 17:23:32 -0400172void __wrap_iounmap(volatile void __iomem *addr)
173{
Dan Williams9bfa8492015-12-11 11:20:16 -0800174 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Dan Williams6bc75612015-06-17 17:23:32 -0400175 if (nfit_res)
176 return;
177 return iounmap(addr);
178}
179EXPORT_SYMBOL(__wrap_iounmap);
180
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600181void __wrap_memunmap(void *addr)
182{
Dan Williams9bfa8492015-12-11 11:20:16 -0800183 struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600184
Ross Zwisler67a3e8f2015-08-27 13:14:20 -0600185 if (nfit_res)
186 return;
187 return memunmap(addr);
188}
189EXPORT_SYMBOL(__wrap_memunmap);
190
Christoph Hellwig708ab622015-08-10 23:07:08 -0400191static struct resource *nfit_test_request_region(struct device *dev,
192 struct resource *parent, resource_size_t start,
193 resource_size_t n, const char *name, int flags)
Dan Williams6bc75612015-06-17 17:23:32 -0400194{
195 struct nfit_test_resource *nfit_res;
196
197 if (parent == &iomem_resource) {
Dan Williams6bc75612015-06-17 17:23:32 -0400198 nfit_res = get_nfit_res(start);
Dan Williams6bc75612015-06-17 17:23:32 -0400199 if (nfit_res) {
200 struct resource *res = nfit_res->res + 1;
201
202 if (start + n > nfit_res->res->start
203 + resource_size(nfit_res->res)) {
204 pr_debug("%s: start: %llx n: %llx overflow: %pr\n",
205 __func__, start, n,
206 nfit_res->res);
207 return NULL;
208 }
209
210 res->start = start;
211 res->end = start + n - 1;
212 res->name = name;
213 res->flags = resource_type(parent);
214 res->flags |= IORESOURCE_BUSY | flags;
215 pr_debug("%s: %pr\n", __func__, res);
216 return res;
217 }
218 }
Christoph Hellwig708ab622015-08-10 23:07:08 -0400219 if (dev)
220 return __devm_request_region(dev, parent, start, n, name);
Dan Williams6bc75612015-06-17 17:23:32 -0400221 return __request_region(parent, start, n, name, flags);
222}
Christoph Hellwig708ab622015-08-10 23:07:08 -0400223
224struct resource *__wrap___request_region(struct resource *parent,
225 resource_size_t start, resource_size_t n, const char *name,
226 int flags)
227{
228 return nfit_test_request_region(NULL, parent, start, n, name, flags);
229}
Dan Williams6bc75612015-06-17 17:23:32 -0400230EXPORT_SYMBOL(__wrap___request_region);
231
Christoph Hellwig708ab622015-08-10 23:07:08 -0400232struct resource *__wrap___devm_request_region(struct device *dev,
233 struct resource *parent, resource_size_t start,
234 resource_size_t n, const char *name)
235{
236 if (!dev)
237 return NULL;
238 return nfit_test_request_region(dev, parent, start, n, name, 0);
239}
240EXPORT_SYMBOL(__wrap___devm_request_region);
241
Dan Williams6bc75612015-06-17 17:23:32 -0400242void __wrap___release_region(struct resource *parent, resource_size_t start,
243 resource_size_t n)
244{
245 struct nfit_test_resource *nfit_res;
246
247 if (parent == &iomem_resource) {
Dan Williams6bc75612015-06-17 17:23:32 -0400248 nfit_res = get_nfit_res(start);
Dan Williams6bc75612015-06-17 17:23:32 -0400249 if (nfit_res) {
250 struct resource *res = nfit_res->res + 1;
251
252 if (start != res->start || resource_size(res) != n)
253 pr_info("%s: start: %llx n: %llx mismatch: %pr\n",
254 __func__, start, n, res);
255 else
256 memset(res, 0, sizeof(*res));
257 return;
258 }
259 }
260 __release_region(parent, start, n);
261}
262EXPORT_SYMBOL(__wrap___release_region);
263
264MODULE_LICENSE("GPL v2");