162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/err.h>
362306a36Sopenharmony_ci#include <linux/pci.h>
462306a36Sopenharmony_ci#include <linux/io.h>
562306a36Sopenharmony_ci#include <linux/gfp.h>
662306a36Sopenharmony_ci#include <linux/export.h>
762306a36Sopenharmony_ci#include <linux/of_address.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_cienum devm_ioremap_type {
1062306a36Sopenharmony_ci	DEVM_IOREMAP = 0,
1162306a36Sopenharmony_ci	DEVM_IOREMAP_UC,
1262306a36Sopenharmony_ci	DEVM_IOREMAP_WC,
1362306a36Sopenharmony_ci	DEVM_IOREMAP_NP,
1462306a36Sopenharmony_ci};
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_civoid devm_ioremap_release(struct device *dev, void *res)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	iounmap(*(void __iomem **)res);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int devm_ioremap_match(struct device *dev, void *res, void *match_data)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	return *(void **)res == match_data;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void __iomem *__devm_ioremap(struct device *dev, resource_size_t offset,
2762306a36Sopenharmony_ci				    resource_size_t size,
2862306a36Sopenharmony_ci				    enum devm_ioremap_type type)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	void __iomem **ptr, *addr = NULL;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ptr = devres_alloc_node(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL,
3362306a36Sopenharmony_ci				dev_to_node(dev));
3462306a36Sopenharmony_ci	if (!ptr)
3562306a36Sopenharmony_ci		return NULL;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	switch (type) {
3862306a36Sopenharmony_ci	case DEVM_IOREMAP:
3962306a36Sopenharmony_ci		addr = ioremap(offset, size);
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	case DEVM_IOREMAP_UC:
4262306a36Sopenharmony_ci		addr = ioremap_uc(offset, size);
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	case DEVM_IOREMAP_WC:
4562306a36Sopenharmony_ci		addr = ioremap_wc(offset, size);
4662306a36Sopenharmony_ci		break;
4762306a36Sopenharmony_ci	case DEVM_IOREMAP_NP:
4862306a36Sopenharmony_ci		addr = ioremap_np(offset, size);
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (addr) {
5362306a36Sopenharmony_ci		*ptr = addr;
5462306a36Sopenharmony_ci		devres_add(dev, ptr);
5562306a36Sopenharmony_ci	} else
5662306a36Sopenharmony_ci		devres_free(ptr);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return addr;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/**
6262306a36Sopenharmony_ci * devm_ioremap - Managed ioremap()
6362306a36Sopenharmony_ci * @dev: Generic device to remap IO address for
6462306a36Sopenharmony_ci * @offset: Resource address to map
6562306a36Sopenharmony_ci * @size: Size of map
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Managed ioremap().  Map is automatically unmapped on driver detach.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_civoid __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
7062306a36Sopenharmony_ci			   resource_size_t size)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return __devm_ioremap(dev, offset, size, DEVM_IOREMAP);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/**
7762306a36Sopenharmony_ci * devm_ioremap_uc - Managed ioremap_uc()
7862306a36Sopenharmony_ci * @dev: Generic device to remap IO address for
7962306a36Sopenharmony_ci * @offset: Resource address to map
8062306a36Sopenharmony_ci * @size: Size of map
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Managed ioremap_uc().  Map is automatically unmapped on driver detach.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_civoid __iomem *devm_ioremap_uc(struct device *dev, resource_size_t offset,
8562306a36Sopenharmony_ci			      resource_size_t size)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_UC);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_ioremap_uc);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/**
9262306a36Sopenharmony_ci * devm_ioremap_wc - Managed ioremap_wc()
9362306a36Sopenharmony_ci * @dev: Generic device to remap IO address for
9462306a36Sopenharmony_ci * @offset: Resource address to map
9562306a36Sopenharmony_ci * @size: Size of map
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Managed ioremap_wc().  Map is automatically unmapped on driver detach.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_civoid __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset,
10062306a36Sopenharmony_ci			      resource_size_t size)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_WC);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap_wc);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * devm_iounmap - Managed iounmap()
10862306a36Sopenharmony_ci * @dev: Generic device to unmap for
10962306a36Sopenharmony_ci * @addr: Address to unmap
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Managed iounmap().  @addr must have been mapped using devm_ioremap*().
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_civoid devm_iounmap(struct device *dev, void __iomem *addr)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match,
11662306a36Sopenharmony_ci			       (__force void *)addr));
11762306a36Sopenharmony_ci	iounmap(addr);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ciEXPORT_SYMBOL(devm_iounmap);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void __iomem *
12262306a36Sopenharmony_ci__devm_ioremap_resource(struct device *dev, const struct resource *res,
12362306a36Sopenharmony_ci			enum devm_ioremap_type type)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	resource_size_t size;
12662306a36Sopenharmony_ci	void __iomem *dest_ptr;
12762306a36Sopenharmony_ci	char *pretty_name;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	BUG_ON(!dev);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!res || resource_type(res) != IORESOURCE_MEM) {
13262306a36Sopenharmony_ci		dev_err(dev, "invalid resource %pR\n", res);
13362306a36Sopenharmony_ci		return IOMEM_ERR_PTR(-EINVAL);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (type == DEVM_IOREMAP && res->flags & IORESOURCE_MEM_NONPOSTED)
13762306a36Sopenharmony_ci		type = DEVM_IOREMAP_NP;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	size = resource_size(res);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (res->name)
14262306a36Sopenharmony_ci		pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
14362306a36Sopenharmony_ci					     dev_name(dev), res->name);
14462306a36Sopenharmony_ci	else
14562306a36Sopenharmony_ci		pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!pretty_name) {
14762306a36Sopenharmony_ci		dev_err(dev, "can't generate pretty name for resource %pR\n", res);
14862306a36Sopenharmony_ci		return IOMEM_ERR_PTR(-ENOMEM);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!devm_request_mem_region(dev, res->start, size, pretty_name)) {
15262306a36Sopenharmony_ci		dev_err(dev, "can't request region for resource %pR\n", res);
15362306a36Sopenharmony_ci		return IOMEM_ERR_PTR(-EBUSY);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	dest_ptr = __devm_ioremap(dev, res->start, size, type);
15762306a36Sopenharmony_ci	if (!dest_ptr) {
15862306a36Sopenharmony_ci		dev_err(dev, "ioremap failed for resource %pR\n", res);
15962306a36Sopenharmony_ci		devm_release_mem_region(dev, res->start, size);
16062306a36Sopenharmony_ci		dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return dest_ptr;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/**
16762306a36Sopenharmony_ci * devm_ioremap_resource() - check, request region, and ioremap resource
16862306a36Sopenharmony_ci * @dev: generic device to handle the resource for
16962306a36Sopenharmony_ci * @res: resource to be handled
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * Checks that a resource is a valid memory region, requests the memory
17262306a36Sopenharmony_ci * region and ioremaps it. All operations are managed and will be undone
17362306a36Sopenharmony_ci * on driver detach.
17462306a36Sopenharmony_ci *
17562306a36Sopenharmony_ci * Usage example:
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci *	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17862306a36Sopenharmony_ci *	base = devm_ioremap_resource(&pdev->dev, res);
17962306a36Sopenharmony_ci *	if (IS_ERR(base))
18062306a36Sopenharmony_ci *		return PTR_ERR(base);
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code
18362306a36Sopenharmony_ci * on failure.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_civoid __iomem *devm_ioremap_resource(struct device *dev,
18662306a36Sopenharmony_ci				    const struct resource *res)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	return __devm_ioremap_resource(dev, res, DEVM_IOREMAP);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap_resource);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/**
19362306a36Sopenharmony_ci * devm_ioremap_resource_wc() - write-combined variant of
19462306a36Sopenharmony_ci *				devm_ioremap_resource()
19562306a36Sopenharmony_ci * @dev: generic device to handle the resource for
19662306a36Sopenharmony_ci * @res: resource to be handled
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code
19962306a36Sopenharmony_ci * on failure.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_civoid __iomem *devm_ioremap_resource_wc(struct device *dev,
20262306a36Sopenharmony_ci				       const struct resource *res)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	return __devm_ioremap_resource(dev, res, DEVM_IOREMAP_WC);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * devm_of_iomap - Requests a resource and maps the memory mapped IO
20962306a36Sopenharmony_ci *		   for a given device_node managed by a given device
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * Checks that a resource is a valid memory region, requests the memory
21262306a36Sopenharmony_ci * region and ioremaps it. All operations are managed and will be undone
21362306a36Sopenharmony_ci * on driver detach of the device.
21462306a36Sopenharmony_ci *
21562306a36Sopenharmony_ci * This is to be used when a device requests/maps resources described
21662306a36Sopenharmony_ci * by other device tree nodes (children or otherwise).
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * @dev:	The device "managing" the resource
21962306a36Sopenharmony_ci * @node:       The device-tree node where the resource resides
22062306a36Sopenharmony_ci * @index:	index of the MMIO range in the "reg" property
22162306a36Sopenharmony_ci * @size:	Returns the size of the resource (pass NULL if not needed)
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Usage example:
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci *	base = devm_of_iomap(&pdev->dev, node, 0, NULL);
22662306a36Sopenharmony_ci *	if (IS_ERR(base))
22762306a36Sopenharmony_ci *		return PTR_ERR(base);
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Please Note: This is not a one-to-one replacement for of_iomap() because the
23062306a36Sopenharmony_ci * of_iomap() function does not track whether the region is already mapped.  If
23162306a36Sopenharmony_ci * two drivers try to map the same memory, the of_iomap() function will succeed
23262306a36Sopenharmony_ci * but the devm_of_iomap() function will return -EBUSY.
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * Return: a pointer to the requested and mapped memory or an ERR_PTR() encoded
23562306a36Sopenharmony_ci * error code on failure.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_civoid __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index,
23862306a36Sopenharmony_ci			    resource_size_t *size)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct resource res;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (of_address_to_resource(node, index, &res))
24362306a36Sopenharmony_ci		return IOMEM_ERR_PTR(-EINVAL);
24462306a36Sopenharmony_ci	if (size)
24562306a36Sopenharmony_ci		*size = resource_size(&res);
24662306a36Sopenharmony_ci	return devm_ioremap_resource(dev, &res);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ciEXPORT_SYMBOL(devm_of_iomap);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci#ifdef CONFIG_HAS_IOPORT_MAP
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci * Generic iomap devres
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic void devm_ioport_map_release(struct device *dev, void *res)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	ioport_unmap(*(void __iomem **)res);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int devm_ioport_map_match(struct device *dev, void *res,
26062306a36Sopenharmony_ci				 void *match_data)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	return *(void **)res == match_data;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * devm_ioport_map - Managed ioport_map()
26762306a36Sopenharmony_ci * @dev: Generic device to map ioport for
26862306a36Sopenharmony_ci * @port: Port to map
26962306a36Sopenharmony_ci * @nr: Number of ports to map
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * Managed ioport_map().  Map is automatically unmapped on driver
27262306a36Sopenharmony_ci * detach.
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * Return: a pointer to the remapped memory or NULL on failure.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_civoid __iomem *devm_ioport_map(struct device *dev, unsigned long port,
27762306a36Sopenharmony_ci			       unsigned int nr)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	void __iomem **ptr, *addr;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ptr = devres_alloc_node(devm_ioport_map_release, sizeof(*ptr), GFP_KERNEL,
28262306a36Sopenharmony_ci				dev_to_node(dev));
28362306a36Sopenharmony_ci	if (!ptr)
28462306a36Sopenharmony_ci		return NULL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	addr = ioport_map(port, nr);
28762306a36Sopenharmony_ci	if (addr) {
28862306a36Sopenharmony_ci		*ptr = addr;
28962306a36Sopenharmony_ci		devres_add(dev, ptr);
29062306a36Sopenharmony_ci	} else
29162306a36Sopenharmony_ci		devres_free(ptr);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return addr;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ciEXPORT_SYMBOL(devm_ioport_map);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/**
29862306a36Sopenharmony_ci * devm_ioport_unmap - Managed ioport_unmap()
29962306a36Sopenharmony_ci * @dev: Generic device to unmap for
30062306a36Sopenharmony_ci * @addr: Address to unmap
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * Managed ioport_unmap().  @addr must have been mapped using
30362306a36Sopenharmony_ci * devm_ioport_map().
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_civoid devm_ioport_unmap(struct device *dev, void __iomem *addr)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	ioport_unmap(addr);
30862306a36Sopenharmony_ci	WARN_ON(devres_destroy(dev, devm_ioport_map_release,
30962306a36Sopenharmony_ci			       devm_ioport_map_match, (__force void *)addr));
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ciEXPORT_SYMBOL(devm_ioport_unmap);
31262306a36Sopenharmony_ci#endif /* CONFIG_HAS_IOPORT_MAP */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci#ifdef CONFIG_PCI
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * PCI iomap devres
31762306a36Sopenharmony_ci */
31862306a36Sopenharmony_ci#define PCIM_IOMAP_MAX	PCI_STD_NUM_BARS
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistruct pcim_iomap_devres {
32162306a36Sopenharmony_ci	void __iomem *table[PCIM_IOMAP_MAX];
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void pcim_iomap_release(struct device *gendev, void *res)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct pci_dev *dev = to_pci_dev(gendev);
32762306a36Sopenharmony_ci	struct pcim_iomap_devres *this = res;
32862306a36Sopenharmony_ci	int i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	for (i = 0; i < PCIM_IOMAP_MAX; i++)
33162306a36Sopenharmony_ci		if (this->table[i])
33262306a36Sopenharmony_ci			pci_iounmap(dev, this->table[i]);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/**
33662306a36Sopenharmony_ci * pcim_iomap_table - access iomap allocation table
33762306a36Sopenharmony_ci * @pdev: PCI device to access iomap table for
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * Access iomap allocation table for @dev.  If iomap table doesn't
34062306a36Sopenharmony_ci * exist and @pdev is managed, it will be allocated.  All iomaps
34162306a36Sopenharmony_ci * recorded in the iomap table are automatically unmapped on driver
34262306a36Sopenharmony_ci * detach.
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * This function might sleep when the table is first allocated but can
34562306a36Sopenharmony_ci * be safely called without context and guaranteed to succeed once
34662306a36Sopenharmony_ci * allocated.
34762306a36Sopenharmony_ci */
34862306a36Sopenharmony_civoid __iomem * const *pcim_iomap_table(struct pci_dev *pdev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct pcim_iomap_devres *dr, *new_dr;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL);
35362306a36Sopenharmony_ci	if (dr)
35462306a36Sopenharmony_ci		return dr->table;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	new_dr = devres_alloc_node(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL,
35762306a36Sopenharmony_ci				   dev_to_node(&pdev->dev));
35862306a36Sopenharmony_ci	if (!new_dr)
35962306a36Sopenharmony_ci		return NULL;
36062306a36Sopenharmony_ci	dr = devres_get(&pdev->dev, new_dr, NULL, NULL);
36162306a36Sopenharmony_ci	return dr->table;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_table);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/**
36662306a36Sopenharmony_ci * pcim_iomap - Managed pcim_iomap()
36762306a36Sopenharmony_ci * @pdev: PCI device to iomap for
36862306a36Sopenharmony_ci * @bar: BAR to iomap
36962306a36Sopenharmony_ci * @maxlen: Maximum length of iomap
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * Managed pci_iomap().  Map is automatically unmapped on driver
37262306a36Sopenharmony_ci * detach.
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_civoid __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	void __iomem **tbl;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	BUG_ON(bar >= PCIM_IOMAP_MAX);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	tbl = (void __iomem **)pcim_iomap_table(pdev);
38162306a36Sopenharmony_ci	if (!tbl || tbl[bar])	/* duplicate mappings not allowed */
38262306a36Sopenharmony_ci		return NULL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	tbl[bar] = pci_iomap(pdev, bar, maxlen);
38562306a36Sopenharmony_ci	return tbl[bar];
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/**
39062306a36Sopenharmony_ci * pcim_iounmap - Managed pci_iounmap()
39162306a36Sopenharmony_ci * @pdev: PCI device to iounmap for
39262306a36Sopenharmony_ci * @addr: Address to unmap
39362306a36Sopenharmony_ci *
39462306a36Sopenharmony_ci * Managed pci_iounmap().  @addr must have been mapped using pcim_iomap().
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_civoid pcim_iounmap(struct pci_dev *pdev, void __iomem *addr)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	void __iomem **tbl;
39962306a36Sopenharmony_ci	int i;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	pci_iounmap(pdev, addr);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	tbl = (void __iomem **)pcim_iomap_table(pdev);
40462306a36Sopenharmony_ci	BUG_ON(!tbl);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	for (i = 0; i < PCIM_IOMAP_MAX; i++)
40762306a36Sopenharmony_ci		if (tbl[i] == addr) {
40862306a36Sopenharmony_ci			tbl[i] = NULL;
40962306a36Sopenharmony_ci			return;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci	WARN_ON(1);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iounmap);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * pcim_iomap_regions - Request and iomap PCI BARs
41762306a36Sopenharmony_ci * @pdev: PCI device to map IO resources for
41862306a36Sopenharmony_ci * @mask: Mask of BARs to request and iomap
41962306a36Sopenharmony_ci * @name: Name used when requesting regions
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci * Request and iomap regions specified by @mask.
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_ciint pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	void __iomem * const *iomap;
42662306a36Sopenharmony_ci	int i, rc;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	iomap = pcim_iomap_table(pdev);
42962306a36Sopenharmony_ci	if (!iomap)
43062306a36Sopenharmony_ci		return -ENOMEM;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
43362306a36Sopenharmony_ci		unsigned long len;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (!(mask & (1 << i)))
43662306a36Sopenharmony_ci			continue;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		rc = -EINVAL;
43962306a36Sopenharmony_ci		len = pci_resource_len(pdev, i);
44062306a36Sopenharmony_ci		if (!len)
44162306a36Sopenharmony_ci			goto err_inval;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		rc = pci_request_region(pdev, i, name);
44462306a36Sopenharmony_ci		if (rc)
44562306a36Sopenharmony_ci			goto err_inval;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		rc = -ENOMEM;
44862306a36Sopenharmony_ci		if (!pcim_iomap(pdev, i, 0))
44962306a36Sopenharmony_ci			goto err_region;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci err_region:
45562306a36Sopenharmony_ci	pci_release_region(pdev, i);
45662306a36Sopenharmony_ci err_inval:
45762306a36Sopenharmony_ci	while (--i >= 0) {
45862306a36Sopenharmony_ci		if (!(mask & (1 << i)))
45962306a36Sopenharmony_ci			continue;
46062306a36Sopenharmony_ci		pcim_iounmap(pdev, iomap[i]);
46162306a36Sopenharmony_ci		pci_release_region(pdev, i);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return rc;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_regions);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/**
46962306a36Sopenharmony_ci * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones
47062306a36Sopenharmony_ci * @pdev: PCI device to map IO resources for
47162306a36Sopenharmony_ci * @mask: Mask of BARs to iomap
47262306a36Sopenharmony_ci * @name: Name used when requesting regions
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * Request all PCI BARs and iomap regions specified by @mask.
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_ciint pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask,
47762306a36Sopenharmony_ci				   const char *name)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	int request_mask = ((1 << 6) - 1) & ~mask;
48062306a36Sopenharmony_ci	int rc;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	rc = pci_request_selected_regions(pdev, request_mask, name);
48362306a36Sopenharmony_ci	if (rc)
48462306a36Sopenharmony_ci		return rc;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	rc = pcim_iomap_regions(pdev, mask, name);
48762306a36Sopenharmony_ci	if (rc)
48862306a36Sopenharmony_ci		pci_release_selected_regions(pdev, request_mask);
48962306a36Sopenharmony_ci	return rc;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_regions_request_all);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/**
49462306a36Sopenharmony_ci * pcim_iounmap_regions - Unmap and release PCI BARs
49562306a36Sopenharmony_ci * @pdev: PCI device to map IO resources for
49662306a36Sopenharmony_ci * @mask: Mask of BARs to unmap and release
49762306a36Sopenharmony_ci *
49862306a36Sopenharmony_ci * Unmap and release regions specified by @mask.
49962306a36Sopenharmony_ci */
50062306a36Sopenharmony_civoid pcim_iounmap_regions(struct pci_dev *pdev, int mask)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	void __iomem * const *iomap;
50362306a36Sopenharmony_ci	int i;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	iomap = pcim_iomap_table(pdev);
50662306a36Sopenharmony_ci	if (!iomap)
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for (i = 0; i < PCIM_IOMAP_MAX; i++) {
51062306a36Sopenharmony_ci		if (!(mask & (1 << i)))
51162306a36Sopenharmony_ci			continue;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		pcim_iounmap(pdev, iomap[i]);
51462306a36Sopenharmony_ci		pci_release_region(pdev, i);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL(pcim_iounmap_regions);
51862306a36Sopenharmony_ci#endif /* CONFIG_PCI */
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void devm_arch_phys_ac_add_release(struct device *dev, void *res)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	arch_phys_wc_del(*((int *)res));
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/**
52662306a36Sopenharmony_ci * devm_arch_phys_wc_add - Managed arch_phys_wc_add()
52762306a36Sopenharmony_ci * @dev: Managed device
52862306a36Sopenharmony_ci * @base: Memory base address
52962306a36Sopenharmony_ci * @size: Size of memory range
53062306a36Sopenharmony_ci *
53162306a36Sopenharmony_ci * Adds a WC MTRR using arch_phys_wc_add() and sets up a release callback.
53262306a36Sopenharmony_ci * See arch_phys_wc_add() for more information.
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_ciint devm_arch_phys_wc_add(struct device *dev, unsigned long base, unsigned long size)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int *mtrr;
53762306a36Sopenharmony_ci	int ret;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	mtrr = devres_alloc_node(devm_arch_phys_ac_add_release, sizeof(*mtrr), GFP_KERNEL,
54062306a36Sopenharmony_ci				 dev_to_node(dev));
54162306a36Sopenharmony_ci	if (!mtrr)
54262306a36Sopenharmony_ci		return -ENOMEM;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = arch_phys_wc_add(base, size);
54562306a36Sopenharmony_ci	if (ret < 0) {
54662306a36Sopenharmony_ci		devres_free(mtrr);
54762306a36Sopenharmony_ci		return ret;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	*mtrr = ret;
55162306a36Sopenharmony_ci	devres_add(dev, mtrr);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return ret;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ciEXPORT_SYMBOL(devm_arch_phys_wc_add);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistruct arch_io_reserve_memtype_wc_devres {
55862306a36Sopenharmony_ci	resource_size_t start;
55962306a36Sopenharmony_ci	resource_size_t size;
56062306a36Sopenharmony_ci};
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void devm_arch_io_free_memtype_wc_release(struct device *dev, void *res)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	const struct arch_io_reserve_memtype_wc_devres *this = res;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	arch_io_free_memtype_wc(this->start, this->size);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * devm_arch_io_reserve_memtype_wc - Managed arch_io_reserve_memtype_wc()
57162306a36Sopenharmony_ci * @dev: Managed device
57262306a36Sopenharmony_ci * @start: Memory base address
57362306a36Sopenharmony_ci * @size: Size of memory range
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * Reserves a memory range with WC caching using arch_io_reserve_memtype_wc()
57662306a36Sopenharmony_ci * and sets up a release callback See arch_io_reserve_memtype_wc() for more
57762306a36Sopenharmony_ci * information.
57862306a36Sopenharmony_ci */
57962306a36Sopenharmony_ciint devm_arch_io_reserve_memtype_wc(struct device *dev, resource_size_t start,
58062306a36Sopenharmony_ci				    resource_size_t size)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct arch_io_reserve_memtype_wc_devres *dr;
58362306a36Sopenharmony_ci	int ret;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	dr = devres_alloc_node(devm_arch_io_free_memtype_wc_release, sizeof(*dr), GFP_KERNEL,
58662306a36Sopenharmony_ci			       dev_to_node(dev));
58762306a36Sopenharmony_ci	if (!dr)
58862306a36Sopenharmony_ci		return -ENOMEM;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	ret = arch_io_reserve_memtype_wc(start, size);
59162306a36Sopenharmony_ci	if (ret < 0) {
59262306a36Sopenharmony_ci		devres_free(dr);
59362306a36Sopenharmony_ci		return ret;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	dr->start = start;
59762306a36Sopenharmony_ci	dr->size = size;
59862306a36Sopenharmony_ci	devres_add(dev, dr);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return ret;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ciEXPORT_SYMBOL(devm_arch_io_reserve_memtype_wc);
603