18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/err.h> 38c2ecf20Sopenharmony_ci#include <linux/pci.h> 48c2ecf20Sopenharmony_ci#include <linux/io.h> 58c2ecf20Sopenharmony_ci#include <linux/gfp.h> 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include <linux/of_address.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cienum devm_ioremap_type { 108c2ecf20Sopenharmony_ci DEVM_IOREMAP = 0, 118c2ecf20Sopenharmony_ci DEVM_IOREMAP_UC, 128c2ecf20Sopenharmony_ci DEVM_IOREMAP_WC, 138c2ecf20Sopenharmony_ci}; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_civoid devm_ioremap_release(struct device *dev, void *res) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci iounmap(*(void __iomem **)res); 188c2ecf20Sopenharmony_ci} 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int devm_ioremap_match(struct device *dev, void *res, void *match_data) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci return *(void **)res == match_data; 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void __iomem *__devm_ioremap(struct device *dev, resource_size_t offset, 268c2ecf20Sopenharmony_ci resource_size_t size, 278c2ecf20Sopenharmony_ci enum devm_ioremap_type type) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci void __iomem **ptr, *addr = NULL; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); 328c2ecf20Sopenharmony_ci if (!ptr) 338c2ecf20Sopenharmony_ci return NULL; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci switch (type) { 368c2ecf20Sopenharmony_ci case DEVM_IOREMAP: 378c2ecf20Sopenharmony_ci addr = ioremap(offset, size); 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci case DEVM_IOREMAP_UC: 408c2ecf20Sopenharmony_ci addr = ioremap_uc(offset, size); 418c2ecf20Sopenharmony_ci break; 428c2ecf20Sopenharmony_ci case DEVM_IOREMAP_WC: 438c2ecf20Sopenharmony_ci addr = ioremap_wc(offset, size); 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (addr) { 488c2ecf20Sopenharmony_ci *ptr = addr; 498c2ecf20Sopenharmony_ci devres_add(dev, ptr); 508c2ecf20Sopenharmony_ci } else 518c2ecf20Sopenharmony_ci devres_free(ptr); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return addr; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * devm_ioremap - Managed ioremap() 588c2ecf20Sopenharmony_ci * @dev: Generic device to remap IO address for 598c2ecf20Sopenharmony_ci * @offset: Resource address to map 608c2ecf20Sopenharmony_ci * @size: Size of map 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Managed ioremap(). Map is automatically unmapped on driver detach. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_civoid __iomem *devm_ioremap(struct device *dev, resource_size_t offset, 658c2ecf20Sopenharmony_ci resource_size_t size) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return __devm_ioremap(dev, offset, size, DEVM_IOREMAP); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * devm_ioremap_uc - Managed ioremap_uc() 738c2ecf20Sopenharmony_ci * @dev: Generic device to remap IO address for 748c2ecf20Sopenharmony_ci * @offset: Resource address to map 758c2ecf20Sopenharmony_ci * @size: Size of map 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Managed ioremap_uc(). Map is automatically unmapped on driver detach. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_civoid __iomem *devm_ioremap_uc(struct device *dev, resource_size_t offset, 808c2ecf20Sopenharmony_ci resource_size_t size) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_UC); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_ioremap_uc); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/** 878c2ecf20Sopenharmony_ci * devm_ioremap_wc - Managed ioremap_wc() 888c2ecf20Sopenharmony_ci * @dev: Generic device to remap IO address for 898c2ecf20Sopenharmony_ci * @offset: Resource address to map 908c2ecf20Sopenharmony_ci * @size: Size of map 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Managed ioremap_wc(). Map is automatically unmapped on driver detach. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_civoid __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, 958c2ecf20Sopenharmony_ci resource_size_t size) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_WC); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap_wc); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * devm_iounmap - Managed iounmap() 1038c2ecf20Sopenharmony_ci * @dev: Generic device to unmap for 1048c2ecf20Sopenharmony_ci * @addr: Address to unmap 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * Managed iounmap(). @addr must have been mapped using devm_ioremap*(). 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_civoid devm_iounmap(struct device *dev, void __iomem *addr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match, 1118c2ecf20Sopenharmony_ci (__force void *)addr)); 1128c2ecf20Sopenharmony_ci iounmap(addr); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_iounmap); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void __iomem * 1178c2ecf20Sopenharmony_ci__devm_ioremap_resource(struct device *dev, const struct resource *res, 1188c2ecf20Sopenharmony_ci enum devm_ioremap_type type) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci resource_size_t size; 1218c2ecf20Sopenharmony_ci void __iomem *dest_ptr; 1228c2ecf20Sopenharmony_ci char *pretty_name; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci BUG_ON(!dev); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!res || resource_type(res) != IORESOURCE_MEM) { 1278c2ecf20Sopenharmony_ci dev_err(dev, "invalid resource\n"); 1288c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(-EINVAL); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci size = resource_size(res); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (res->name) 1348c2ecf20Sopenharmony_ci pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", 1358c2ecf20Sopenharmony_ci dev_name(dev), res->name); 1368c2ecf20Sopenharmony_ci else 1378c2ecf20Sopenharmony_ci pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); 1388c2ecf20Sopenharmony_ci if (!pretty_name) 1398c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!devm_request_mem_region(dev, res->start, size, pretty_name)) { 1428c2ecf20Sopenharmony_ci dev_err(dev, "can't request region for resource %pR\n", res); 1438c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(-EBUSY); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dest_ptr = __devm_ioremap(dev, res->start, size, type); 1478c2ecf20Sopenharmony_ci if (!dest_ptr) { 1488c2ecf20Sopenharmony_ci dev_err(dev, "ioremap failed for resource %pR\n", res); 1498c2ecf20Sopenharmony_ci devm_release_mem_region(dev, res->start, size); 1508c2ecf20Sopenharmony_ci dest_ptr = IOMEM_ERR_PTR(-ENOMEM); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return dest_ptr; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * devm_ioremap_resource() - check, request region, and ioremap resource 1588c2ecf20Sopenharmony_ci * @dev: generic device to handle the resource for 1598c2ecf20Sopenharmony_ci * @res: resource to be handled 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * Checks that a resource is a valid memory region, requests the memory 1628c2ecf20Sopenharmony_ci * region and ioremaps it. All operations are managed and will be undone 1638c2ecf20Sopenharmony_ci * on driver detach. 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Usage example: 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1688c2ecf20Sopenharmony_ci * base = devm_ioremap_resource(&pdev->dev, res); 1698c2ecf20Sopenharmony_ci * if (IS_ERR(base)) 1708c2ecf20Sopenharmony_ci * return PTR_ERR(base); 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 1738c2ecf20Sopenharmony_ci * on failure. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_civoid __iomem *devm_ioremap_resource(struct device *dev, 1768c2ecf20Sopenharmony_ci const struct resource *res) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return __devm_ioremap_resource(dev, res, DEVM_IOREMAP); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_ioremap_resource); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * devm_ioremap_resource_wc() - write-combined variant of 1848c2ecf20Sopenharmony_ci * devm_ioremap_resource() 1858c2ecf20Sopenharmony_ci * @dev: generic device to handle the resource for 1868c2ecf20Sopenharmony_ci * @res: resource to be handled 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 1898c2ecf20Sopenharmony_ci * on failure. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_civoid __iomem *devm_ioremap_resource_wc(struct device *dev, 1928c2ecf20Sopenharmony_ci const struct resource *res) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return __devm_ioremap_resource(dev, res, DEVM_IOREMAP_WC); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * devm_of_iomap - Requests a resource and maps the memory mapped IO 1998c2ecf20Sopenharmony_ci * for a given device_node managed by a given device 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Checks that a resource is a valid memory region, requests the memory 2028c2ecf20Sopenharmony_ci * region and ioremaps it. All operations are managed and will be undone 2038c2ecf20Sopenharmony_ci * on driver detach of the device. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * This is to be used when a device requests/maps resources described 2068c2ecf20Sopenharmony_ci * by other device tree nodes (children or otherwise). 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * @dev: The device "managing" the resource 2098c2ecf20Sopenharmony_ci * @node: The device-tree node where the resource resides 2108c2ecf20Sopenharmony_ci * @index: index of the MMIO range in the "reg" property 2118c2ecf20Sopenharmony_ci * @size: Returns the size of the resource (pass NULL if not needed) 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Usage example: 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * base = devm_of_iomap(&pdev->dev, node, 0, NULL); 2168c2ecf20Sopenharmony_ci * if (IS_ERR(base)) 2178c2ecf20Sopenharmony_ci * return PTR_ERR(base); 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Please Note: This is not a one-to-one replacement for of_iomap() because the 2208c2ecf20Sopenharmony_ci * of_iomap() function does not track whether the region is already mapped. If 2218c2ecf20Sopenharmony_ci * two drivers try to map the same memory, the of_iomap() function will succeed 2228c2ecf20Sopenharmony_ci * but the devm_of_iomap() function will return -EBUSY. 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * Return: a pointer to the requested and mapped memory or an ERR_PTR() encoded 2258c2ecf20Sopenharmony_ci * error code on failure. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_civoid __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index, 2288c2ecf20Sopenharmony_ci resource_size_t *size) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct resource res; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (of_address_to_resource(node, index, &res)) 2338c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(-EINVAL); 2348c2ecf20Sopenharmony_ci if (size) 2358c2ecf20Sopenharmony_ci *size = resource_size(&res); 2368c2ecf20Sopenharmony_ci return devm_ioremap_resource(dev, &res); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_of_iomap); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci#ifdef CONFIG_HAS_IOPORT_MAP 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * Generic iomap devres 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic void devm_ioport_map_release(struct device *dev, void *res) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci ioport_unmap(*(void __iomem **)res); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int devm_ioport_map_match(struct device *dev, void *res, 2508c2ecf20Sopenharmony_ci void *match_data) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci return *(void **)res == match_data; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/** 2568c2ecf20Sopenharmony_ci * devm_ioport_map - Managed ioport_map() 2578c2ecf20Sopenharmony_ci * @dev: Generic device to map ioport for 2588c2ecf20Sopenharmony_ci * @port: Port to map 2598c2ecf20Sopenharmony_ci * @nr: Number of ports to map 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Managed ioport_map(). Map is automatically unmapped on driver 2628c2ecf20Sopenharmony_ci * detach. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or NULL on failure. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_civoid __iomem *devm_ioport_map(struct device *dev, unsigned long port, 2678c2ecf20Sopenharmony_ci unsigned int nr) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci void __iomem **ptr, *addr; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_ioport_map_release, sizeof(*ptr), GFP_KERNEL); 2728c2ecf20Sopenharmony_ci if (!ptr) 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci addr = ioport_map(port, nr); 2768c2ecf20Sopenharmony_ci if (addr) { 2778c2ecf20Sopenharmony_ci *ptr = addr; 2788c2ecf20Sopenharmony_ci devres_add(dev, ptr); 2798c2ecf20Sopenharmony_ci } else 2808c2ecf20Sopenharmony_ci devres_free(ptr); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return addr; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_ioport_map); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/** 2878c2ecf20Sopenharmony_ci * devm_ioport_unmap - Managed ioport_unmap() 2888c2ecf20Sopenharmony_ci * @dev: Generic device to unmap for 2898c2ecf20Sopenharmony_ci * @addr: Address to unmap 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Managed ioport_unmap(). @addr must have been mapped using 2928c2ecf20Sopenharmony_ci * devm_ioport_map(). 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_civoid devm_ioport_unmap(struct device *dev, void __iomem *addr) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci ioport_unmap(addr); 2978c2ecf20Sopenharmony_ci WARN_ON(devres_destroy(dev, devm_ioport_map_release, 2988c2ecf20Sopenharmony_ci devm_ioport_map_match, (__force void *)addr)); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_ioport_unmap); 3018c2ecf20Sopenharmony_ci#endif /* CONFIG_HAS_IOPORT_MAP */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * PCI iomap devres 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci#define PCIM_IOMAP_MAX PCI_STD_NUM_BARS 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistruct pcim_iomap_devres { 3108c2ecf20Sopenharmony_ci void __iomem *table[PCIM_IOMAP_MAX]; 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void pcim_iomap_release(struct device *gendev, void *res) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct pci_dev *dev = to_pci_dev(gendev); 3168c2ecf20Sopenharmony_ci struct pcim_iomap_devres *this = res; 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; i < PCIM_IOMAP_MAX; i++) 3208c2ecf20Sopenharmony_ci if (this->table[i]) 3218c2ecf20Sopenharmony_ci pci_iounmap(dev, this->table[i]); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/** 3258c2ecf20Sopenharmony_ci * pcim_iomap_table - access iomap allocation table 3268c2ecf20Sopenharmony_ci * @pdev: PCI device to access iomap table for 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * Access iomap allocation table for @dev. If iomap table doesn't 3298c2ecf20Sopenharmony_ci * exist and @pdev is managed, it will be allocated. All iomaps 3308c2ecf20Sopenharmony_ci * recorded in the iomap table are automatically unmapped on driver 3318c2ecf20Sopenharmony_ci * detach. 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * This function might sleep when the table is first allocated but can 3348c2ecf20Sopenharmony_ci * be safely called without context and guaranteed to succed once 3358c2ecf20Sopenharmony_ci * allocated. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_civoid __iomem * const *pcim_iomap_table(struct pci_dev *pdev) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct pcim_iomap_devres *dr, *new_dr; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL); 3428c2ecf20Sopenharmony_ci if (dr) 3438c2ecf20Sopenharmony_ci return dr->table; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci new_dr = devres_alloc(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL); 3468c2ecf20Sopenharmony_ci if (!new_dr) 3478c2ecf20Sopenharmony_ci return NULL; 3488c2ecf20Sopenharmony_ci dr = devres_get(&pdev->dev, new_dr, NULL, NULL); 3498c2ecf20Sopenharmony_ci return dr->table; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_table); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/** 3548c2ecf20Sopenharmony_ci * pcim_iomap - Managed pcim_iomap() 3558c2ecf20Sopenharmony_ci * @pdev: PCI device to iomap for 3568c2ecf20Sopenharmony_ci * @bar: BAR to iomap 3578c2ecf20Sopenharmony_ci * @maxlen: Maximum length of iomap 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Managed pci_iomap(). Map is automatically unmapped on driver 3608c2ecf20Sopenharmony_ci * detach. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_civoid __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci void __iomem **tbl; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci BUG_ON(bar >= PCIM_IOMAP_MAX); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci tbl = (void __iomem **)pcim_iomap_table(pdev); 3698c2ecf20Sopenharmony_ci if (!tbl || tbl[bar]) /* duplicate mappings not allowed */ 3708c2ecf20Sopenharmony_ci return NULL; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci tbl[bar] = pci_iomap(pdev, bar, maxlen); 3738c2ecf20Sopenharmony_ci return tbl[bar]; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * pcim_iounmap - Managed pci_iounmap() 3798c2ecf20Sopenharmony_ci * @pdev: PCI device to iounmap for 3808c2ecf20Sopenharmony_ci * @addr: Address to unmap 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap(). 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_civoid pcim_iounmap(struct pci_dev *pdev, void __iomem *addr) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci void __iomem **tbl; 3878c2ecf20Sopenharmony_ci int i; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci pci_iounmap(pdev, addr); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci tbl = (void __iomem **)pcim_iomap_table(pdev); 3928c2ecf20Sopenharmony_ci BUG_ON(!tbl); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (i = 0; i < PCIM_IOMAP_MAX; i++) 3958c2ecf20Sopenharmony_ci if (tbl[i] == addr) { 3968c2ecf20Sopenharmony_ci tbl[i] = NULL; 3978c2ecf20Sopenharmony_ci return; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci WARN_ON(1); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iounmap); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * pcim_iomap_regions - Request and iomap PCI BARs 4058c2ecf20Sopenharmony_ci * @pdev: PCI device to map IO resources for 4068c2ecf20Sopenharmony_ci * @mask: Mask of BARs to request and iomap 4078c2ecf20Sopenharmony_ci * @name: Name used when requesting regions 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Request and iomap regions specified by @mask. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ciint pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci void __iomem * const *iomap; 4148c2ecf20Sopenharmony_ci int i, rc; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci iomap = pcim_iomap_table(pdev); 4178c2ecf20Sopenharmony_ci if (!iomap) 4188c2ecf20Sopenharmony_ci return -ENOMEM; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 4218c2ecf20Sopenharmony_ci unsigned long len; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!(mask & (1 << i))) 4248c2ecf20Sopenharmony_ci continue; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci rc = -EINVAL; 4278c2ecf20Sopenharmony_ci len = pci_resource_len(pdev, i); 4288c2ecf20Sopenharmony_ci if (!len) 4298c2ecf20Sopenharmony_ci goto err_inval; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci rc = pci_request_region(pdev, i, name); 4328c2ecf20Sopenharmony_ci if (rc) 4338c2ecf20Sopenharmony_ci goto err_inval; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci rc = -ENOMEM; 4368c2ecf20Sopenharmony_ci if (!pcim_iomap(pdev, i, 0)) 4378c2ecf20Sopenharmony_ci goto err_region; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci err_region: 4438c2ecf20Sopenharmony_ci pci_release_region(pdev, i); 4448c2ecf20Sopenharmony_ci err_inval: 4458c2ecf20Sopenharmony_ci while (--i >= 0) { 4468c2ecf20Sopenharmony_ci if (!(mask & (1 << i))) 4478c2ecf20Sopenharmony_ci continue; 4488c2ecf20Sopenharmony_ci pcim_iounmap(pdev, iomap[i]); 4498c2ecf20Sopenharmony_ci pci_release_region(pdev, i); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return rc; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_regions); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/** 4578c2ecf20Sopenharmony_ci * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones 4588c2ecf20Sopenharmony_ci * @pdev: PCI device to map IO resources for 4598c2ecf20Sopenharmony_ci * @mask: Mask of BARs to iomap 4608c2ecf20Sopenharmony_ci * @name: Name used when requesting regions 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * Request all PCI BARs and iomap regions specified by @mask. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ciint pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, 4658c2ecf20Sopenharmony_ci const char *name) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci int request_mask = ((1 << 6) - 1) & ~mask; 4688c2ecf20Sopenharmony_ci int rc; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci rc = pci_request_selected_regions(pdev, request_mask, name); 4718c2ecf20Sopenharmony_ci if (rc) 4728c2ecf20Sopenharmony_ci return rc; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci rc = pcim_iomap_regions(pdev, mask, name); 4758c2ecf20Sopenharmony_ci if (rc) 4768c2ecf20Sopenharmony_ci pci_release_selected_regions(pdev, request_mask); 4778c2ecf20Sopenharmony_ci return rc; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iomap_regions_request_all); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/** 4828c2ecf20Sopenharmony_ci * pcim_iounmap_regions - Unmap and release PCI BARs 4838c2ecf20Sopenharmony_ci * @pdev: PCI device to map IO resources for 4848c2ecf20Sopenharmony_ci * @mask: Mask of BARs to unmap and release 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * Unmap and release regions specified by @mask. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_civoid pcim_iounmap_regions(struct pci_dev *pdev, int mask) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci void __iomem * const *iomap; 4918c2ecf20Sopenharmony_ci int i; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci iomap = pcim_iomap_table(pdev); 4948c2ecf20Sopenharmony_ci if (!iomap) 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (i = 0; i < PCIM_IOMAP_MAX; i++) { 4988c2ecf20Sopenharmony_ci if (!(mask & (1 << i))) 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci pcim_iounmap(pdev, iomap[i]); 5028c2ecf20Sopenharmony_ci pci_release_region(pdev, i); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcim_iounmap_regions); 5068c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */ 507