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> | |
19 | #include "nfit_test.h" | |
20 | ||
21 | static LIST_HEAD(iomap_head); | |
22 | ||
23 | static 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 | ||
30 | void 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 | } | |
35 | EXPORT_SYMBOL(nfit_test_setup); | |
36 | ||
37 | void nfit_test_teardown(void) | |
38 | { | |
39 | list_del_rcu(&iomap_ops.list); | |
40 | synchronize_rcu(); | |
41 | } | |
42 | EXPORT_SYMBOL(nfit_test_teardown); | |
43 | ||
44 | static 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 | ||
54 | void __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 | ||
9d27a87e DW |
68 | void __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 | } | |
81 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); | |
82 | ||
708ab62b CH |
83 | void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, |
84 | size_t size, unsigned long flags) | |
6bc75619 | 85 | { |
e836a256 DW |
86 | 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) | |
92 | return (void __iomem *) nfit_res->buf + offset | |
93 | - nfit_res->res->start; | |
708ab62b | 94 | return devm_memremap(dev, offset, size, flags); |
6bc75619 | 95 | } |
708ab62b | 96 | EXPORT_SYMBOL(__wrap_devm_memremap); |
6bc75619 DW |
97 | |
98 | void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size) | |
99 | { | |
100 | return __nfit_test_ioremap(offset, size, ioremap_nocache); | |
101 | } | |
102 | EXPORT_SYMBOL(__wrap_ioremap_nocache); | |
103 | ||
9d27a87e DW |
104 | void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size) |
105 | { | |
106 | return __nfit_test_ioremap(offset, size, ioremap_wc); | |
107 | } | |
108 | EXPORT_SYMBOL(__wrap_ioremap_wc); | |
109 | ||
6bc75619 DW |
110 | void __wrap_iounmap(volatile void __iomem *addr) |
111 | { | |
112 | struct nfit_test_resource *nfit_res; | |
113 | ||
114 | rcu_read_lock(); | |
115 | nfit_res = get_nfit_res((unsigned long) addr); | |
116 | rcu_read_unlock(); | |
117 | if (nfit_res) | |
118 | return; | |
119 | return iounmap(addr); | |
120 | } | |
121 | EXPORT_SYMBOL(__wrap_iounmap); | |
122 | ||
708ab62b CH |
123 | static struct resource *nfit_test_request_region(struct device *dev, |
124 | struct resource *parent, resource_size_t start, | |
125 | resource_size_t n, const char *name, int flags) | |
6bc75619 DW |
126 | { |
127 | struct nfit_test_resource *nfit_res; | |
128 | ||
129 | if (parent == &iomem_resource) { | |
130 | rcu_read_lock(); | |
131 | nfit_res = get_nfit_res(start); | |
132 | rcu_read_unlock(); | |
133 | if (nfit_res) { | |
134 | struct resource *res = nfit_res->res + 1; | |
135 | ||
136 | if (start + n > nfit_res->res->start | |
137 | + resource_size(nfit_res->res)) { | |
138 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", | |
139 | __func__, start, n, | |
140 | nfit_res->res); | |
141 | return NULL; | |
142 | } | |
143 | ||
144 | res->start = start; | |
145 | res->end = start + n - 1; | |
146 | res->name = name; | |
147 | res->flags = resource_type(parent); | |
148 | res->flags |= IORESOURCE_BUSY | flags; | |
149 | pr_debug("%s: %pr\n", __func__, res); | |
150 | return res; | |
151 | } | |
152 | } | |
708ab62b CH |
153 | if (dev) |
154 | return __devm_request_region(dev, parent, start, n, name); | |
6bc75619 DW |
155 | return __request_region(parent, start, n, name, flags); |
156 | } | |
708ab62b CH |
157 | |
158 | struct resource *__wrap___request_region(struct resource *parent, | |
159 | resource_size_t start, resource_size_t n, const char *name, | |
160 | int flags) | |
161 | { | |
162 | return nfit_test_request_region(NULL, parent, start, n, name, flags); | |
163 | } | |
6bc75619 DW |
164 | EXPORT_SYMBOL(__wrap___request_region); |
165 | ||
708ab62b CH |
166 | struct resource *__wrap___devm_request_region(struct device *dev, |
167 | struct resource *parent, resource_size_t start, | |
168 | resource_size_t n, const char *name) | |
169 | { | |
170 | if (!dev) | |
171 | return NULL; | |
172 | return nfit_test_request_region(dev, parent, start, n, name, 0); | |
173 | } | |
174 | EXPORT_SYMBOL(__wrap___devm_request_region); | |
175 | ||
6bc75619 DW |
176 | void __wrap___release_region(struct resource *parent, resource_size_t start, |
177 | resource_size_t n) | |
178 | { | |
179 | struct nfit_test_resource *nfit_res; | |
180 | ||
181 | if (parent == &iomem_resource) { | |
182 | rcu_read_lock(); | |
183 | nfit_res = get_nfit_res(start); | |
184 | rcu_read_unlock(); | |
185 | if (nfit_res) { | |
186 | struct resource *res = nfit_res->res + 1; | |
187 | ||
188 | if (start != res->start || resource_size(res) != n) | |
189 | pr_info("%s: start: %llx n: %llx mismatch: %pr\n", | |
190 | __func__, start, n, res); | |
191 | else | |
192 | memset(res, 0, sizeof(*res)); | |
193 | return; | |
194 | } | |
195 | } | |
196 | __release_region(parent, start, n); | |
197 | } | |
198 | EXPORT_SYMBOL(__wrap___release_region); | |
199 | ||
200 | MODULE_LICENSE("GPL v2"); |