Blame view

lib/devres.c 10.3 KB
f4a18312f   Thierry Reding   lib: devres: Fix ...
1
  #include <linux/err.h>
5ea817699   Al Viro   [PATCH] sort the ...
2
3
  #include <linux/pci.h>
  #include <linux/io.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
4
  #include <linux/gfp.h>
8bc3bcc93   Paul Gortmaker   lib: reduce the u...
5
  #include <linux/export.h>
5ea817699   Al Viro   [PATCH] sort the ...
6

b41e5fffe   Emil Medve   [POWERPC] devres:...
7
  void devm_ioremap_release(struct device *dev, void *res)
5ea817699   Al Viro   [PATCH] sort the ...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  {
  	iounmap(*(void __iomem **)res);
  }
  
  static int devm_ioremap_match(struct device *dev, void *res, void *match_data)
  {
  	return *(void **)res == match_data;
  }
  
  /**
   * devm_ioremap - Managed ioremap()
   * @dev: Generic device to remap IO address for
   * @offset: BUS offset to map
   * @size: Size of map
   *
   * Managed ioremap().  Map is automatically unmapped on driver detach.
   */
4f452e8aa   Kumar Gala   devres: support a...
25
  void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
5ea817699   Al Viro   [PATCH] sort the ...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  			   unsigned long size)
  {
  	void __iomem **ptr, *addr;
  
  	ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return NULL;
  
  	addr = ioremap(offset, size);
  	if (addr) {
  		*ptr = addr;
  		devres_add(dev, ptr);
  	} else
  		devres_free(ptr);
  
  	return addr;
  }
  EXPORT_SYMBOL(devm_ioremap);
  
  /**
   * devm_ioremap_nocache - Managed ioremap_nocache()
   * @dev: Generic device to remap IO address for
   * @offset: BUS offset to map
   * @size: Size of map
   *
   * Managed ioremap_nocache().  Map is automatically unmapped on driver
   * detach.
   */
4f452e8aa   Kumar Gala   devres: support a...
54
  void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
5ea817699   Al Viro   [PATCH] sort the ...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  				   unsigned long size)
  {
  	void __iomem **ptr, *addr;
  
  	ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return NULL;
  
  	addr = ioremap_nocache(offset, size);
  	if (addr) {
  		*ptr = addr;
  		devres_add(dev, ptr);
  	} else
  		devres_free(ptr);
  
  	return addr;
  }
  EXPORT_SYMBOL(devm_ioremap_nocache);
  
  /**
   * devm_iounmap - Managed iounmap()
   * @dev: Generic device to unmap for
   * @addr: Address to unmap
   *
   * Managed iounmap().  @addr must have been mapped using devm_ioremap*().
   */
  void devm_iounmap(struct device *dev, void __iomem *addr)
  {
5ea817699   Al Viro   [PATCH] sort the ...
83
  	WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match,
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
84
  			       (__force void *)addr));
ae891a1b9   Maxin B John   devres: fix possi...
85
  	iounmap(addr);
5ea817699   Al Viro   [PATCH] sort the ...
86
87
  }
  EXPORT_SYMBOL(devm_iounmap);
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
88
  #define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err)
72f8c0bfa   Wolfram Sang   lib: devres: add ...
89
  /**
75096579c   Thierry Reding   lib: devres: Intr...
90
91
   * devm_ioremap_resource() - check, request region, and ioremap resource
   * @dev: generic device to handle the resource for
72f8c0bfa   Wolfram Sang   lib: devres: add ...
92
93
   * @res: resource to be handled
   *
75096579c   Thierry Reding   lib: devres: Intr...
94
95
96
97
98
99
100
   * Checks that a resource is a valid memory region, requests the memory region
   * and ioremaps it either as cacheable or as non-cacheable memory depending on
   * the resource's flags. All operations are managed and will be undone on
   * driver detach.
   *
   * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code
   * on failure. Usage example:
72f8c0bfa   Wolfram Sang   lib: devres: add ...
101
102
   *
   *	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
75096579c   Thierry Reding   lib: devres: Intr...
103
104
105
   *	base = devm_ioremap_resource(&pdev->dev, res);
   *	if (IS_ERR(base))
   *		return PTR_ERR(base);
72f8c0bfa   Wolfram Sang   lib: devres: add ...
106
   */
75096579c   Thierry Reding   lib: devres: Intr...
107
  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
72f8c0bfa   Wolfram Sang   lib: devres: add ...
108
109
110
111
112
113
114
115
116
117
  {
  	resource_size_t size;
  	const char *name;
  	void __iomem *dest_ptr;
  
  	BUG_ON(!dev);
  
  	if (!res || resource_type(res) != IORESOURCE_MEM) {
  		dev_err(dev, "invalid resource
  ");
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
118
  		return IOMEM_ERR_PTR(-EINVAL);
72f8c0bfa   Wolfram Sang   lib: devres: add ...
119
120
121
122
123
124
125
126
  	}
  
  	size = resource_size(res);
  	name = res->name ?: dev_name(dev);
  
  	if (!devm_request_mem_region(dev, res->start, size, name)) {
  		dev_err(dev, "can't request region for resource %pR
  ", res);
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
127
  		return IOMEM_ERR_PTR(-EBUSY);
72f8c0bfa   Wolfram Sang   lib: devres: add ...
128
129
130
131
132
133
134
135
136
137
138
  	}
  
  	if (res->flags & IORESOURCE_CACHEABLE)
  		dest_ptr = devm_ioremap(dev, res->start, size);
  	else
  		dest_ptr = devm_ioremap_nocache(dev, res->start, size);
  
  	if (!dest_ptr) {
  		dev_err(dev, "ioremap failed for resource %pR
  ", res);
  		devm_release_mem_region(dev, res->start, size);
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
139
  		dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
72f8c0bfa   Wolfram Sang   lib: devres: add ...
140
141
142
143
  	}
  
  	return dest_ptr;
  }
75096579c   Thierry Reding   lib: devres: Intr...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  EXPORT_SYMBOL(devm_ioremap_resource);
  
  /**
   * devm_request_and_ioremap() - Check, request region, and ioremap resource
   * @dev: Generic device to handle the resource for
   * @res: resource to be handled
   *
   * Takes all necessary steps to ioremap a mem resource. Uses managed device, so
   * everything is undone on driver detach. Checks arguments, so you can feed
   * it the result from e.g. platform_get_resource() directly. Returns the
   * remapped pointer or NULL on error. Usage example:
   *
   *	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   *	base = devm_request_and_ioremap(&pdev->dev, res);
   *	if (!base)
   *		return -EADDRNOTAVAIL;
   */
  void __iomem *devm_request_and_ioremap(struct device *device,
  				       struct resource *res)
  {
  	void __iomem *dest_ptr;
  
  	dest_ptr = devm_ioremap_resource(device, res);
  	if (IS_ERR(dest_ptr))
  		return NULL;
  
  	return dest_ptr;
  }
72f8c0bfa   Wolfram Sang   lib: devres: add ...
172
  EXPORT_SYMBOL(devm_request_and_ioremap);
ce816fa88   Uwe Kleine-König   Kconfig: rename H...
173
  #ifdef CONFIG_HAS_IOPORT_MAP
5ea817699   Al Viro   [PATCH] sort the ...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  /*
   * Generic iomap devres
   */
  static void devm_ioport_map_release(struct device *dev, void *res)
  {
  	ioport_unmap(*(void __iomem **)res);
  }
  
  static int devm_ioport_map_match(struct device *dev, void *res,
  				 void *match_data)
  {
  	return *(void **)res == match_data;
  }
  
  /**
   * devm_ioport_map - Managed ioport_map()
   * @dev: Generic device to map ioport for
   * @port: Port to map
   * @nr: Number of ports to map
   *
   * Managed ioport_map().  Map is automatically unmapped on driver
   * detach.
   */
  void __iomem * devm_ioport_map(struct device *dev, unsigned long port,
  			       unsigned int nr)
  {
  	void __iomem **ptr, *addr;
  
  	ptr = devres_alloc(devm_ioport_map_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return NULL;
  
  	addr = ioport_map(port, nr);
  	if (addr) {
  		*ptr = addr;
  		devres_add(dev, ptr);
  	} else
  		devres_free(ptr);
  
  	return addr;
  }
  EXPORT_SYMBOL(devm_ioport_map);
  
  /**
   * devm_ioport_unmap - Managed ioport_unmap()
   * @dev: Generic device to unmap for
   * @addr: Address to unmap
   *
   * Managed ioport_unmap().  @addr must have been mapped using
   * devm_ioport_map().
   */
  void devm_ioport_unmap(struct device *dev, void __iomem *addr)
  {
  	ioport_unmap(addr);
  	WARN_ON(devres_destroy(dev, devm_ioport_map_release,
b104d6a5a   Steven Rostedt   lib/devres.c: fix...
229
  			       devm_ioport_map_match, (__force void *)addr));
5ea817699   Al Viro   [PATCH] sort the ...
230
231
  }
  EXPORT_SYMBOL(devm_ioport_unmap);
ce816fa88   Uwe Kleine-König   Kconfig: rename H...
232
  #endif /* CONFIG_HAS_IOPORT_MAP */
5ea817699   Al Viro   [PATCH] sort the ...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  
  #ifdef CONFIG_PCI
  /*
   * PCI iomap devres
   */
  #define PCIM_IOMAP_MAX	PCI_ROM_RESOURCE
  
  struct pcim_iomap_devres {
  	void __iomem *table[PCIM_IOMAP_MAX];
  };
  
  static void pcim_iomap_release(struct device *gendev, void *res)
  {
  	struct pci_dev *dev = container_of(gendev, struct pci_dev, dev);
  	struct pcim_iomap_devres *this = res;
  	int i;
  
  	for (i = 0; i < PCIM_IOMAP_MAX; i++)
  		if (this->table[i])
  			pci_iounmap(dev, this->table[i]);
  }
  
  /**
   * pcim_iomap_table - access iomap allocation table
   * @pdev: PCI device to access iomap table for
   *
   * Access iomap allocation table for @dev.  If iomap table doesn't
   * exist and @pdev is managed, it will be allocated.  All iomaps
   * recorded in the iomap table are automatically unmapped on driver
   * detach.
   *
   * This function might sleep when the table is first allocated but can
   * be safely called without context and guaranteed to succed once
   * allocated.
   */
  void __iomem * const * pcim_iomap_table(struct pci_dev *pdev)
  {
  	struct pcim_iomap_devres *dr, *new_dr;
  
  	dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL);
  	if (dr)
  		return dr->table;
  
  	new_dr = devres_alloc(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL);
  	if (!new_dr)
  		return NULL;
  	dr = devres_get(&pdev->dev, new_dr, NULL, NULL);
  	return dr->table;
  }
  EXPORT_SYMBOL(pcim_iomap_table);
  
  /**
   * pcim_iomap - Managed pcim_iomap()
   * @pdev: PCI device to iomap for
   * @bar: BAR to iomap
   * @maxlen: Maximum length of iomap
   *
   * Managed pci_iomap().  Map is automatically unmapped on driver
   * detach.
   */
  void __iomem * pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
  {
  	void __iomem **tbl;
  
  	BUG_ON(bar >= PCIM_IOMAP_MAX);
  
  	tbl = (void __iomem **)pcim_iomap_table(pdev);
  	if (!tbl || tbl[bar])	/* duplicate mappings not allowed */
  		return NULL;
  
  	tbl[bar] = pci_iomap(pdev, bar, maxlen);
  	return tbl[bar];
  }
  EXPORT_SYMBOL(pcim_iomap);
  
  /**
   * pcim_iounmap - Managed pci_iounmap()
   * @pdev: PCI device to iounmap for
   * @addr: Address to unmap
   *
   * Managed pci_iounmap().  @addr must have been mapped using pcim_iomap().
   */
  void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr)
  {
  	void __iomem **tbl;
  	int i;
  
  	pci_iounmap(pdev, addr);
  
  	tbl = (void __iomem **)pcim_iomap_table(pdev);
  	BUG_ON(!tbl);
  
  	for (i = 0; i < PCIM_IOMAP_MAX; i++)
  		if (tbl[i] == addr) {
  			tbl[i] = NULL;
  			return;
  		}
  	WARN_ON(1);
  }
  EXPORT_SYMBOL(pcim_iounmap);
  
  /**
   * pcim_iomap_regions - Request and iomap PCI BARs
   * @pdev: PCI device to map IO resources for
   * @mask: Mask of BARs to request and iomap
   * @name: Name used when requesting regions
   *
   * Request and iomap regions specified by @mask.
   */
fb7ebfe41   Yinghai Lu   PCI: Increase res...
342
  int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name)
5ea817699   Al Viro   [PATCH] sort the ...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  {
  	void __iomem * const *iomap;
  	int i, rc;
  
  	iomap = pcim_iomap_table(pdev);
  	if (!iomap)
  		return -ENOMEM;
  
  	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
  		unsigned long len;
  
  		if (!(mask & (1 << i)))
  			continue;
  
  		rc = -EINVAL;
  		len = pci_resource_len(pdev, i);
  		if (!len)
  			goto err_inval;
  
  		rc = pci_request_region(pdev, i, name);
  		if (rc)
fb4d64e78   Frederik Deweerdt   [PATCH] pci_iomap...
364
  			goto err_inval;
5ea817699   Al Viro   [PATCH] sort the ...
365
366
367
  
  		rc = -ENOMEM;
  		if (!pcim_iomap(pdev, i, 0))
fb4d64e78   Frederik Deweerdt   [PATCH] pci_iomap...
368
  			goto err_region;
5ea817699   Al Viro   [PATCH] sort the ...
369
370
371
  	}
  
  	return 0;
5ea817699   Al Viro   [PATCH] sort the ...
372
373
374
375
   err_region:
  	pci_release_region(pdev, i);
   err_inval:
  	while (--i >= 0) {
fb4d64e78   Frederik Deweerdt   [PATCH] pci_iomap...
376
377
  		if (!(mask & (1 << i)))
  			continue;
5ea817699   Al Viro   [PATCH] sort the ...
378
379
380
381
382
383
384
  		pcim_iounmap(pdev, iomap[i]);
  		pci_release_region(pdev, i);
  	}
  
  	return rc;
  }
  EXPORT_SYMBOL(pcim_iomap_regions);
ec04b0758   Tejun Heo   iomap: implement ...
385
386
  
  /**
916fbfb7a   Tejun Heo   devres: implement...
387
388
389
390
391
392
393
   * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones
   * @pdev: PCI device to map IO resources for
   * @mask: Mask of BARs to iomap
   * @name: Name used when requesting regions
   *
   * Request all PCI BARs and iomap regions specified by @mask.
   */
fb7ebfe41   Yinghai Lu   PCI: Increase res...
394
  int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask,
916fbfb7a   Tejun Heo   devres: implement...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  				   const char *name)
  {
  	int request_mask = ((1 << 6) - 1) & ~mask;
  	int rc;
  
  	rc = pci_request_selected_regions(pdev, request_mask, name);
  	if (rc)
  		return rc;
  
  	rc = pcim_iomap_regions(pdev, mask, name);
  	if (rc)
  		pci_release_selected_regions(pdev, request_mask);
  	return rc;
  }
  EXPORT_SYMBOL(pcim_iomap_regions_request_all);
  
  /**
ec04b0758   Tejun Heo   iomap: implement ...
412
413
414
415
   * pcim_iounmap_regions - Unmap and release PCI BARs
   * @pdev: PCI device to map IO resources for
   * @mask: Mask of BARs to unmap and release
   *
4d45ada36   Kulikov Vasiliy   lib/devres.c: fix...
416
   * Unmap and release regions specified by @mask.
ec04b0758   Tejun Heo   iomap: implement ...
417
   */
fb7ebfe41   Yinghai Lu   PCI: Increase res...
418
  void pcim_iounmap_regions(struct pci_dev *pdev, int mask)
ec04b0758   Tejun Heo   iomap: implement ...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  {
  	void __iomem * const *iomap;
  	int i;
  
  	iomap = pcim_iomap_table(pdev);
  	if (!iomap)
  		return;
  
  	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
  		if (!(mask & (1 << i)))
  			continue;
  
  		pcim_iounmap(pdev, iomap[i]);
  		pci_release_region(pdev, i);
  	}
  }
  EXPORT_SYMBOL(pcim_iounmap_regions);
571806a9f   Wolfram Sang   lib: devres: add ...
436
  #endif /* CONFIG_PCI */