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