diff options
Diffstat (limited to 'lib/devres.c')
-rw-r--r-- | lib/devres.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/lib/devres.c b/lib/devres.c new file mode 100644 index 000000000000..2a668dd7cac7 --- /dev/null +++ b/lib/devres.c | |||
@@ -0,0 +1,300 @@ | |||
1 | #include <linux/pci.h> | ||
2 | #include <linux/io.h> | ||
3 | #include <linux/module.h> | ||
4 | |||
5 | static void devm_ioremap_release(struct device *dev, void *res) | ||
6 | { | ||
7 | iounmap(*(void __iomem **)res); | ||
8 | } | ||
9 | |||
10 | static int devm_ioremap_match(struct device *dev, void *res, void *match_data) | ||
11 | { | ||
12 | return *(void **)res == match_data; | ||
13 | } | ||
14 | |||
15 | /** | ||
16 | * devm_ioremap - Managed ioremap() | ||
17 | * @dev: Generic device to remap IO address for | ||
18 | * @offset: BUS offset to map | ||
19 | * @size: Size of map | ||
20 | * | ||
21 | * Managed ioremap(). Map is automatically unmapped on driver detach. | ||
22 | */ | ||
23 | void __iomem *devm_ioremap(struct device *dev, unsigned long offset, | ||
24 | unsigned long size) | ||
25 | { | ||
26 | void __iomem **ptr, *addr; | ||
27 | |||
28 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); | ||
29 | if (!ptr) | ||
30 | return NULL; | ||
31 | |||
32 | addr = ioremap(offset, size); | ||
33 | if (addr) { | ||
34 | *ptr = addr; | ||
35 | devres_add(dev, ptr); | ||
36 | } else | ||
37 | devres_free(ptr); | ||
38 | |||
39 | return addr; | ||
40 | } | ||
41 | EXPORT_SYMBOL(devm_ioremap); | ||
42 | |||
43 | /** | ||
44 | * devm_ioremap_nocache - Managed ioremap_nocache() | ||
45 | * @dev: Generic device to remap IO address for | ||
46 | * @offset: BUS offset to map | ||
47 | * @size: Size of map | ||
48 | * | ||
49 | * Managed ioremap_nocache(). Map is automatically unmapped on driver | ||
50 | * detach. | ||
51 | */ | ||
52 | void __iomem *devm_ioremap_nocache(struct device *dev, unsigned long offset, | ||
53 | unsigned long size) | ||
54 | { | ||
55 | void __iomem **ptr, *addr; | ||
56 | |||
57 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); | ||
58 | if (!ptr) | ||
59 | return NULL; | ||
60 | |||
61 | addr = ioremap_nocache(offset, size); | ||
62 | if (addr) { | ||
63 | *ptr = addr; | ||
64 | devres_add(dev, ptr); | ||
65 | } else | ||
66 | devres_free(ptr); | ||
67 | |||
68 | return addr; | ||
69 | } | ||
70 | EXPORT_SYMBOL(devm_ioremap_nocache); | ||
71 | |||
72 | /** | ||
73 | * devm_iounmap - Managed iounmap() | ||
74 | * @dev: Generic device to unmap for | ||
75 | * @addr: Address to unmap | ||
76 | * | ||
77 | * Managed iounmap(). @addr must have been mapped using devm_ioremap*(). | ||
78 | */ | ||
79 | void devm_iounmap(struct device *dev, void __iomem *addr) | ||
80 | { | ||
81 | iounmap(addr); | ||
82 | WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match, | ||
83 | (void *)addr)); | ||
84 | } | ||
85 | EXPORT_SYMBOL(devm_iounmap); | ||
86 | |||
87 | #ifdef CONFIG_HAS_IOPORT | ||
88 | /* | ||
89 | * Generic iomap devres | ||
90 | */ | ||
91 | static void devm_ioport_map_release(struct device *dev, void *res) | ||
92 | { | ||
93 | ioport_unmap(*(void __iomem **)res); | ||
94 | } | ||
95 | |||
96 | static int devm_ioport_map_match(struct device *dev, void *res, | ||
97 | void *match_data) | ||
98 | { | ||
99 | return *(void **)res == match_data; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * devm_ioport_map - Managed ioport_map() | ||
104 | * @dev: Generic device to map ioport for | ||
105 | * @port: Port to map | ||
106 | * @nr: Number of ports to map | ||
107 | * | ||
108 | * Managed ioport_map(). Map is automatically unmapped on driver | ||
109 | * detach. | ||
110 | */ | ||
111 | void __iomem * devm_ioport_map(struct device *dev, unsigned long port, | ||
112 | unsigned int nr) | ||
113 | { | ||
114 | void __iomem **ptr, *addr; | ||
115 | |||
116 | ptr = devres_alloc(devm_ioport_map_release, sizeof(*ptr), GFP_KERNEL); | ||
117 | if (!ptr) | ||
118 | return NULL; | ||
119 | |||
120 | addr = ioport_map(port, nr); | ||
121 | if (addr) { | ||
122 | *ptr = addr; | ||
123 | devres_add(dev, ptr); | ||
124 | } else | ||
125 | devres_free(ptr); | ||
126 | |||
127 | return addr; | ||
128 | } | ||
129 | EXPORT_SYMBOL(devm_ioport_map); | ||
130 | |||
131 | /** | ||
132 | * devm_ioport_unmap - Managed ioport_unmap() | ||
133 | * @dev: Generic device to unmap for | ||
134 | * @addr: Address to unmap | ||
135 | * | ||
136 | * Managed ioport_unmap(). @addr must have been mapped using | ||
137 | * devm_ioport_map(). | ||
138 | */ | ||
139 | void devm_ioport_unmap(struct device *dev, void __iomem *addr) | ||
140 | { | ||
141 | ioport_unmap(addr); | ||
142 | WARN_ON(devres_destroy(dev, devm_ioport_map_release, | ||
143 | devm_ioport_map_match, (void *)addr)); | ||
144 | } | ||
145 | EXPORT_SYMBOL(devm_ioport_unmap); | ||
146 | |||
147 | #ifdef CONFIG_PCI | ||
148 | /* | ||
149 | * PCI iomap devres | ||
150 | */ | ||
151 | #define PCIM_IOMAP_MAX PCI_ROM_RESOURCE | ||
152 | |||
153 | struct pcim_iomap_devres { | ||
154 | void __iomem *table[PCIM_IOMAP_MAX]; | ||
155 | }; | ||
156 | |||
157 | static void pcim_iomap_release(struct device *gendev, void *res) | ||
158 | { | ||
159 | struct pci_dev *dev = container_of(gendev, struct pci_dev, dev); | ||
160 | struct pcim_iomap_devres *this = res; | ||
161 | int i; | ||
162 | |||
163 | for (i = 0; i < PCIM_IOMAP_MAX; i++) | ||
164 | if (this->table[i]) | ||
165 | pci_iounmap(dev, this->table[i]); | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * pcim_iomap_table - access iomap allocation table | ||
170 | * @pdev: PCI device to access iomap table for | ||
171 | * | ||
172 | * Access iomap allocation table for @dev. If iomap table doesn't | ||
173 | * exist and @pdev is managed, it will be allocated. All iomaps | ||
174 | * recorded in the iomap table are automatically unmapped on driver | ||
175 | * detach. | ||
176 | * | ||
177 | * This function might sleep when the table is first allocated but can | ||
178 | * be safely called without context and guaranteed to succed once | ||
179 | * allocated. | ||
180 | */ | ||
181 | void __iomem * const * pcim_iomap_table(struct pci_dev *pdev) | ||
182 | { | ||
183 | struct pcim_iomap_devres *dr, *new_dr; | ||
184 | |||
185 | dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL); | ||
186 | if (dr) | ||
187 | return dr->table; | ||
188 | |||
189 | new_dr = devres_alloc(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL); | ||
190 | if (!new_dr) | ||
191 | return NULL; | ||
192 | dr = devres_get(&pdev->dev, new_dr, NULL, NULL); | ||
193 | return dr->table; | ||
194 | } | ||
195 | EXPORT_SYMBOL(pcim_iomap_table); | ||
196 | |||
197 | /** | ||
198 | * pcim_iomap - Managed pcim_iomap() | ||
199 | * @pdev: PCI device to iomap for | ||
200 | * @bar: BAR to iomap | ||
201 | * @maxlen: Maximum length of iomap | ||
202 | * | ||
203 | * Managed pci_iomap(). Map is automatically unmapped on driver | ||
204 | * detach. | ||
205 | */ | ||
206 | void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) | ||
207 | { | ||
208 | void __iomem **tbl; | ||
209 | |||
210 | BUG_ON(bar >= PCIM_IOMAP_MAX); | ||
211 | |||
212 | tbl = (void __iomem **)pcim_iomap_table(pdev); | ||
213 | if (!tbl || tbl[bar]) /* duplicate mappings not allowed */ | ||
214 | return NULL; | ||
215 | |||
216 | tbl[bar] = pci_iomap(pdev, bar, maxlen); | ||
217 | return tbl[bar]; | ||
218 | } | ||
219 | EXPORT_SYMBOL(pcim_iomap); | ||
220 | |||
221 | /** | ||
222 | * pcim_iounmap - Managed pci_iounmap() | ||
223 | * @pdev: PCI device to iounmap for | ||
224 | * @addr: Address to unmap | ||
225 | * | ||
226 | * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap(). | ||
227 | */ | ||
228 | void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr) | ||
229 | { | ||
230 | void __iomem **tbl; | ||
231 | int i; | ||
232 | |||
233 | pci_iounmap(pdev, addr); | ||
234 | |||
235 | tbl = (void __iomem **)pcim_iomap_table(pdev); | ||
236 | BUG_ON(!tbl); | ||
237 | |||
238 | for (i = 0; i < PCIM_IOMAP_MAX; i++) | ||
239 | if (tbl[i] == addr) { | ||
240 | tbl[i] = NULL; | ||
241 | return; | ||
242 | } | ||
243 | WARN_ON(1); | ||
244 | } | ||
245 | EXPORT_SYMBOL(pcim_iounmap); | ||
246 | |||
247 | /** | ||
248 | * pcim_iomap_regions - Request and iomap PCI BARs | ||
249 | * @pdev: PCI device to map IO resources for | ||
250 | * @mask: Mask of BARs to request and iomap | ||
251 | * @name: Name used when requesting regions | ||
252 | * | ||
253 | * Request and iomap regions specified by @mask. | ||
254 | */ | ||
255 | int pcim_iomap_regions(struct pci_dev *pdev, u16 mask, const char *name) | ||
256 | { | ||
257 | void __iomem * const *iomap; | ||
258 | int i, rc; | ||
259 | |||
260 | iomap = pcim_iomap_table(pdev); | ||
261 | if (!iomap) | ||
262 | return -ENOMEM; | ||
263 | |||
264 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
265 | unsigned long len; | ||
266 | |||
267 | if (!(mask & (1 << i))) | ||
268 | continue; | ||
269 | |||
270 | rc = -EINVAL; | ||
271 | len = pci_resource_len(pdev, i); | ||
272 | if (!len) | ||
273 | goto err_inval; | ||
274 | |||
275 | rc = pci_request_region(pdev, i, name); | ||
276 | if (rc) | ||
277 | goto err_region; | ||
278 | |||
279 | rc = -ENOMEM; | ||
280 | if (!pcim_iomap(pdev, i, 0)) | ||
281 | goto err_iomap; | ||
282 | } | ||
283 | |||
284 | return 0; | ||
285 | |||
286 | err_iomap: | ||
287 | pcim_iounmap(pdev, iomap[i]); | ||
288 | err_region: | ||
289 | pci_release_region(pdev, i); | ||
290 | err_inval: | ||
291 | while (--i >= 0) { | ||
292 | pcim_iounmap(pdev, iomap[i]); | ||
293 | pci_release_region(pdev, i); | ||
294 | } | ||
295 | |||
296 | return rc; | ||
297 | } | ||
298 | EXPORT_SYMBOL(pcim_iomap_regions); | ||
299 | #endif | ||
300 | #endif | ||