Commit | Line | Data |
---|---|---|
6bc75619 DW |
1 | /* |
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> | |
9bfa8496 | 19 | #include <linux/mm.h> |
6bc75619 DW |
20 | #include "nfit_test.h" |
21 | ||
22 | static LIST_HEAD(iomap_head); | |
23 | ||
24 | static 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 | ||
31 | void 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 | } | |
36 | EXPORT_SYMBOL(nfit_test_setup); | |
37 | ||
38 | void nfit_test_teardown(void) | |
39 | { | |
40 | list_del_rcu(&iomap_ops.list); | |
41 | synchronize_rcu(); | |
42 | } | |
43 | EXPORT_SYMBOL(nfit_test_teardown); | |
44 | ||
9bfa8496 | 45 | static struct nfit_test_resource *__get_nfit_res(resource_size_t resource) |
6bc75619 DW |
46 | { |
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 | ||
f295e53b | 55 | struct nfit_test_resource *get_nfit_res(resource_size_t resource) |
6bc75619 | 56 | { |
9bfa8496 | 57 | struct nfit_test_resource *res; |
6bc75619 DW |
58 | |
59 | rcu_read_lock(); | |
9bfa8496 | 60 | res = __get_nfit_res(resource); |
6bc75619 | 61 | rcu_read_unlock(); |
9bfa8496 DW |
62 | |
63 | return res; | |
64 | } | |
f295e53b | 65 | EXPORT_SYMBOL(get_nfit_res); |
9bfa8496 DW |
66 | |
67 | void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, | |
68 | void __iomem *(*fallback_fn)(resource_size_t, unsigned long)) | |
69 | { | |
70 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | |
71 | ||
6bc75619 DW |
72 | if (nfit_res) |
73 | return (void __iomem *) nfit_res->buf + offset | |
74 | - nfit_res->res->start; | |
75 | return fallback_fn(offset, size); | |
76 | } | |
77 | ||
9d27a87e DW |
78 | void __iomem *__wrap_devm_ioremap_nocache(struct device *dev, |
79 | resource_size_t offset, unsigned long size) | |
80 | { | |
9bfa8496 | 81 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
9d27a87e | 82 | |
9d27a87e DW |
83 | if (nfit_res) |
84 | return (void __iomem *) nfit_res->buf + offset | |
85 | - nfit_res->res->start; | |
86 | return devm_ioremap_nocache(dev, offset, size); | |
87 | } | |
88 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); | |
89 | ||
708ab62b CH |
90 | void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, |
91 | size_t size, unsigned long flags) | |
6bc75619 | 92 | { |
9bfa8496 | 93 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
e836a256 | 94 | |
e836a256 | 95 | if (nfit_res) |
67a3e8fe | 96 | return nfit_res->buf + offset - nfit_res->res->start; |
708ab62b | 97 | return devm_memremap(dev, offset, size, flags); |
6bc75619 | 98 | } |
708ab62b | 99 | EXPORT_SYMBOL(__wrap_devm_memremap); |
6bc75619 | 100 | |
979fccfb DW |
101 | #ifdef __HAVE_ARCH_PTE_DEVMAP |
102 | #include <linux/memremap.h> | |
103 | #include <linux/pfn_t.h> | |
104 | ||
105 | void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res, | |
106 | struct percpu_ref *ref, struct vmem_altmap *altmap) | |
107 | { | |
108 | resource_size_t offset = res->start; | |
9bfa8496 | 109 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
979fccfb | 110 | |
979fccfb DW |
111 | if (nfit_res) |
112 | return nfit_res->buf + offset - nfit_res->res->start; | |
113 | return devm_memremap_pages(dev, res, ref, altmap); | |
114 | } | |
115 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); | |
116 | ||
76e9f0ee | 117 | pfn_t __wrap_phys_to_pfn_t(phys_addr_t addr, unsigned long flags) |
979fccfb | 118 | { |
9bfa8496 | 119 | struct nfit_test_resource *nfit_res = get_nfit_res(addr); |
979fccfb | 120 | |
979fccfb DW |
121 | if (nfit_res) |
122 | flags &= ~PFN_MAP; | |
123 | return phys_to_pfn_t(addr, flags); | |
124 | } | |
125 | EXPORT_SYMBOL(__wrap_phys_to_pfn_t); | |
126 | #else | |
127 | /* to be removed post 4.5-rc1 */ | |
128 | void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res) | |
129 | { | |
130 | resource_size_t offset = res->start; | |
9bfa8496 | 131 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
979fccfb | 132 | |
979fccfb DW |
133 | if (nfit_res) |
134 | return nfit_res->buf + offset - nfit_res->res->start; | |
135 | return devm_memremap_pages(dev, res); | |
136 | } | |
137 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); | |
138 | #endif | |
139 | ||
67a3e8fe RZ |
140 | void *__wrap_memremap(resource_size_t offset, size_t size, |
141 | unsigned long flags) | |
142 | { | |
9bfa8496 | 143 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
67a3e8fe | 144 | |
67a3e8fe RZ |
145 | if (nfit_res) |
146 | return nfit_res->buf + offset - nfit_res->res->start; | |
147 | return memremap(offset, size, flags); | |
148 | } | |
149 | EXPORT_SYMBOL(__wrap_memremap); | |
150 | ||
32ab0a3f DW |
151 | void __wrap_devm_memunmap(struct device *dev, void *addr) |
152 | { | |
9bfa8496 | 153 | struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); |
32ab0a3f | 154 | |
32ab0a3f DW |
155 | if (nfit_res) |
156 | return; | |
157 | return devm_memunmap(dev, addr); | |
158 | } | |
159 | EXPORT_SYMBOL(__wrap_devm_memunmap); | |
160 | ||
6bc75619 DW |
161 | void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size) |
162 | { | |
163 | return __nfit_test_ioremap(offset, size, ioremap_nocache); | |
164 | } | |
165 | EXPORT_SYMBOL(__wrap_ioremap_nocache); | |
166 | ||
9d27a87e DW |
167 | void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size) |
168 | { | |
169 | return __nfit_test_ioremap(offset, size, ioremap_wc); | |
170 | } | |
171 | EXPORT_SYMBOL(__wrap_ioremap_wc); | |
172 | ||
6bc75619 DW |
173 | void __wrap_iounmap(volatile void __iomem *addr) |
174 | { | |
9bfa8496 | 175 | struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); |
6bc75619 DW |
176 | if (nfit_res) |
177 | return; | |
178 | return iounmap(addr); | |
179 | } | |
180 | EXPORT_SYMBOL(__wrap_iounmap); | |
181 | ||
67a3e8fe RZ |
182 | void __wrap_memunmap(void *addr) |
183 | { | |
9bfa8496 | 184 | struct nfit_test_resource *nfit_res = get_nfit_res((long) addr); |
67a3e8fe | 185 | |
67a3e8fe RZ |
186 | if (nfit_res) |
187 | return; | |
188 | return memunmap(addr); | |
189 | } | |
190 | EXPORT_SYMBOL(__wrap_memunmap); | |
191 | ||
708ab62b CH |
192 | static struct resource *nfit_test_request_region(struct device *dev, |
193 | struct resource *parent, resource_size_t start, | |
194 | resource_size_t n, const char *name, int flags) | |
6bc75619 DW |
195 | { |
196 | struct nfit_test_resource *nfit_res; | |
197 | ||
198 | if (parent == &iomem_resource) { | |
6bc75619 | 199 | nfit_res = get_nfit_res(start); |
6bc75619 DW |
200 | if (nfit_res) { |
201 | struct resource *res = nfit_res->res + 1; | |
202 | ||
203 | if (start + n > nfit_res->res->start | |
204 | + resource_size(nfit_res->res)) { | |
205 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", | |
206 | __func__, start, n, | |
207 | nfit_res->res); | |
208 | return NULL; | |
209 | } | |
210 | ||
211 | res->start = start; | |
212 | res->end = start + n - 1; | |
213 | res->name = name; | |
214 | res->flags = resource_type(parent); | |
215 | res->flags |= IORESOURCE_BUSY | flags; | |
216 | pr_debug("%s: %pr\n", __func__, res); | |
217 | return res; | |
218 | } | |
219 | } | |
708ab62b CH |
220 | if (dev) |
221 | return __devm_request_region(dev, parent, start, n, name); | |
6bc75619 DW |
222 | return __request_region(parent, start, n, name, flags); |
223 | } | |
708ab62b CH |
224 | |
225 | struct resource *__wrap___request_region(struct resource *parent, | |
226 | resource_size_t start, resource_size_t n, const char *name, | |
227 | int flags) | |
228 | { | |
229 | return nfit_test_request_region(NULL, parent, start, n, name, flags); | |
230 | } | |
6bc75619 DW |
231 | EXPORT_SYMBOL(__wrap___request_region); |
232 | ||
708ab62b CH |
233 | struct resource *__wrap___devm_request_region(struct device *dev, |
234 | struct resource *parent, resource_size_t start, | |
235 | resource_size_t n, const char *name) | |
236 | { | |
237 | if (!dev) | |
238 | return NULL; | |
239 | return nfit_test_request_region(dev, parent, start, n, name, 0); | |
240 | } | |
241 | EXPORT_SYMBOL(__wrap___devm_request_region); | |
242 | ||
200c79da DW |
243 | static bool nfit_test_release_region(struct resource *parent, |
244 | resource_size_t start, resource_size_t n) | |
6bc75619 | 245 | { |
6bc75619 | 246 | if (parent == &iomem_resource) { |
200c79da | 247 | struct nfit_test_resource *nfit_res = get_nfit_res(start); |
6bc75619 DW |
248 | if (nfit_res) { |
249 | struct resource *res = nfit_res->res + 1; | |
250 | ||
251 | if (start != res->start || resource_size(res) != n) | |
252 | pr_info("%s: start: %llx n: %llx mismatch: %pr\n", | |
253 | __func__, start, n, res); | |
254 | else | |
255 | memset(res, 0, sizeof(*res)); | |
200c79da | 256 | return true; |
6bc75619 DW |
257 | } |
258 | } | |
200c79da DW |
259 | return false; |
260 | } | |
261 | ||
262 | void __wrap___release_region(struct resource *parent, resource_size_t start, | |
263 | resource_size_t n) | |
264 | { | |
265 | if (!nfit_test_release_region(parent, start, n)) | |
266 | __release_region(parent, start, n); | |
6bc75619 DW |
267 | } |
268 | EXPORT_SYMBOL(__wrap___release_region); | |
269 | ||
200c79da DW |
270 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, |
271 | resource_size_t start, resource_size_t n) | |
272 | { | |
273 | if (!nfit_test_release_region(parent, start, n)) | |
274 | __devm_release_region(dev, parent, start, n); | |
275 | } | |
276 | EXPORT_SYMBOL(__wrap___devm_release_region); | |
277 | ||
6bc75619 | 278 | MODULE_LICENSE("GPL v2"); |