diff options
-rw-r--r-- | drivers/pci/devres.c | 241 | ||||
-rw-r--r-- | drivers/pci/pci.c | 249 | ||||
-rw-r--r-- | drivers/pci/pci.h | 21 |
3 files changed, 262 insertions, 249 deletions
diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index a3fd0d65cef1..2c562b9eaf80 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/device.h> #include <linux/pci.h> #include "pci.h" @@ -11,6 +12,246 @@ struct pcim_iomap_devres { void __iomem *table[PCIM_IOMAP_MAX]; }; + +static void devm_pci_unmap_iospace(struct device *dev, void *ptr) +{ + struct resource **res = ptr; + + pci_unmap_iospace(*res); +} + +/** + * devm_pci_remap_iospace - Managed pci_remap_iospace() + * @dev: Generic device to remap IO address for + * @res: Resource describing the I/O space + * @phys_addr: physical address of range to be mapped + * + * Managed pci_remap_iospace(). Map is automatically unmapped on driver + * detach. + */ +int devm_pci_remap_iospace(struct device *dev, const struct resource *res, + phys_addr_t phys_addr) +{ + const struct resource **ptr; + int error; + + ptr = devres_alloc(devm_pci_unmap_iospace, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + error = pci_remap_iospace(res, phys_addr); + if (error) { + devres_free(ptr); + } else { + *ptr = res; + devres_add(dev, ptr); + } + + return error; +} +EXPORT_SYMBOL(devm_pci_remap_iospace); + +/** + * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace() + * @dev: Generic device to remap IO address for + * @offset: Resource address to map + * @size: Size of map + * + * Managed pci_remap_cfgspace(). Map is automatically unmapped on driver + * detach. + */ +void __iomem *devm_pci_remap_cfgspace(struct device *dev, + resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr; + + ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + addr = pci_remap_cfgspace(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return addr; +} +EXPORT_SYMBOL(devm_pci_remap_cfgspace); + +/** + * devm_pci_remap_cfg_resource - check, request region and ioremap cfg resource + * @dev: generic device to handle the resource for + * @res: configuration space resource to be handled + * + * Checks that a resource is a valid memory region, requests the memory + * region and ioremaps with pci_remap_cfgspace() API that ensures the + * proper PCI configuration space memory attributes are guaranteed. + * + * 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:: + * + * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + * base = devm_pci_remap_cfg_resource(&pdev->dev, res); + * if (IS_ERR(base)) + * return PTR_ERR(base); + */ +void __iomem *devm_pci_remap_cfg_resource(struct device *dev, + struct resource *res) +{ + 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\n"); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + + if (res->name) + name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", dev_name(dev), + res->name); + else + name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!name) + return IOMEM_ERR_PTR(-ENOMEM); + + if (!devm_request_mem_region(dev, res->start, size, name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return IOMEM_ERR_PTR(-EBUSY); + } + + dest_ptr = devm_pci_remap_cfgspace(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + devm_release_mem_region(dev, res->start, size); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} +EXPORT_SYMBOL(devm_pci_remap_cfg_resource); + +/** + * pcim_set_mwi - a device-managed pci_set_mwi() + * @dev: the PCI device for which MWI is enabled + * + * Managed pci_set_mwi(). + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +int pcim_set_mwi(struct pci_dev *dev) +{ + struct pci_devres *dr; + + dr = find_pci_dr(dev); + if (!dr) + return -ENOMEM; + + dr->mwi = 1; + return pci_set_mwi(dev); +} +EXPORT_SYMBOL(pcim_set_mwi); + + +static void pcim_release(struct device *gendev, void *res) +{ + struct pci_dev *dev = to_pci_dev(gendev); + struct pci_devres *this = res; + int i; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + if (this->region_mask & (1 << i)) + pci_release_region(dev, i); + + if (this->mwi) + pci_clear_mwi(dev); + + if (this->restore_intx) + pci_intx(dev, this->orig_intx); + + if (this->enabled && !this->pinned) + pci_disable_device(dev); +} + +/* + * TODO: After the last four callers in pci.c are ported, find_pci_dr() + * needs to be made static again. + */ +struct pci_devres *find_pci_dr(struct pci_dev *pdev) +{ + if (pci_is_managed(pdev)) + return devres_find(&pdev->dev, pcim_release, NULL, NULL); + return NULL; +} + +static struct pci_devres *get_pci_dr(struct pci_dev *pdev) +{ + struct pci_devres *dr, *new_dr; + + dr = devres_find(&pdev->dev, pcim_release, NULL, NULL); + if (dr) + return dr; + + new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL); + if (!new_dr) + return NULL; + return devres_get(&pdev->dev, new_dr, NULL, NULL); +} + +/** + * pcim_enable_device - Managed pci_enable_device() + * @pdev: PCI device to be initialized + * + * Managed pci_enable_device(). + */ +int pcim_enable_device(struct pci_dev *pdev) +{ + struct pci_devres *dr; + int rc; + + dr = get_pci_dr(pdev); + if (unlikely(!dr)) + return -ENOMEM; + if (dr->enabled) + return 0; + + rc = pci_enable_device(pdev); + if (!rc) { + pdev->is_managed = 1; + dr->enabled = 1; + } + return rc; +} +EXPORT_SYMBOL(pcim_enable_device); + +/** + * pcim_pin_device - Pin managed PCI device + * @pdev: PCI device to pin + * + * Pin managed PCI device @pdev. Pinned device won't be disabled on + * driver detach. @pdev must have been enabled with + * pcim_enable_device(). + */ +void pcim_pin_device(struct pci_dev *pdev) +{ + struct pci_devres *dr; + + dr = find_pci_dr(pdev); + WARN_ON(!dr || !dr->enabled); + if (dr) + dr->pinned = 1; +} +EXPORT_SYMBOL(pcim_pin_device); + static void pcim_iomap_release(struct device *gendev, void *res) { struct pci_dev *dev = to_pci_dev(gendev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d8f11a078924..19f18c3856e8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2158,107 +2158,6 @@ int pci_enable_device(struct pci_dev *dev) EXPORT_SYMBOL(pci_enable_device); /* - * Managed PCI resources. This manages device on/off, INTx/MSI/MSI-X - * on/off and BAR regions. pci_dev itself records MSI/MSI-X status, so - * there's no need to track it separately. pci_devres is initialized - * when a device is enabled using managed PCI device enable interface. - */ -struct pci_devres { - unsigned int enabled:1; - unsigned int pinned:1; - unsigned int orig_intx:1; - unsigned int restore_intx:1; - unsigned int mwi:1; - u32 region_mask; -}; - -static void pcim_release(struct device *gendev, void *res) -{ - struct pci_dev *dev = to_pci_dev(gendev); - struct pci_devres *this = res; - int i; - - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) - if (this->region_mask & (1 << i)) - pci_release_region(dev, i); - - if (this->mwi) - pci_clear_mwi(dev); - - if (this->restore_intx) - pci_intx(dev, this->orig_intx); - - if (this->enabled && !this->pinned) - pci_disable_device(dev); -} - -static struct pci_devres *get_pci_dr(struct pci_dev *pdev) -{ - struct pci_devres *dr, *new_dr; - - dr = devres_find(&pdev->dev, pcim_release, NULL, NULL); - if (dr) - return dr; - - new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL); - if (!new_dr) - return NULL; - return devres_get(&pdev->dev, new_dr, NULL, NULL); -} - -static struct pci_devres *find_pci_dr(struct pci_dev *pdev) -{ - if (pci_is_managed(pdev)) - return devres_find(&pdev->dev, pcim_release, NULL, NULL); - return NULL; -} - -/** - * pcim_enable_device - Managed pci_enable_device() - * @pdev: PCI device to be initialized - * - * Managed pci_enable_device(). - */ -int pcim_enable_device(struct pci_dev *pdev) -{ - struct pci_devres *dr; - int rc; - - dr = get_pci_dr(pdev); - if (unlikely(!dr)) - return -ENOMEM; - if (dr->enabled) - return 0; - - rc = pci_enable_device(pdev); - if (!rc) { - pdev->is_managed = 1; - dr->enabled = 1; - } - return rc; -} -EXPORT_SYMBOL(pcim_enable_device); - -/** - * pcim_pin_device - Pin managed PCI device - * @pdev: PCI device to pin - * - * Pin managed PCI device @pdev. Pinned device won't be disabled on - * driver detach. @pdev must have been enabled with - * pcim_enable_device(). - */ -void pcim_pin_device(struct pci_dev *pdev) -{ - struct pci_devres *dr; - - dr = find_pci_dr(pdev); - WARN_ON(!dr || !dr->enabled); - if (dr) - dr->pinned = 1; -} -EXPORT_SYMBOL(pcim_pin_device); - -/* * pcibios_device_add - provide arch specific hooks when adding device dev * @dev: the PCI device being added * @@ -4352,133 +4251,6 @@ void pci_unmap_iospace(struct resource *res) } EXPORT_SYMBOL(pci_unmap_iospace); -static void devm_pci_unmap_iospace(struct device *dev, void *ptr) -{ - struct resource **res = ptr; - - pci_unmap_iospace(*res); -} - -/** - * devm_pci_remap_iospace - Managed pci_remap_iospace() - * @dev: Generic device to remap IO address for - * @res: Resource describing the I/O space - * @phys_addr: physical address of range to be mapped - * - * Managed pci_remap_iospace(). Map is automatically unmapped on driver - * detach. - */ -int devm_pci_remap_iospace(struct device *dev, const struct resource *res, - phys_addr_t phys_addr) -{ - const struct resource **ptr; - int error; - - ptr = devres_alloc(devm_pci_unmap_iospace, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - error = pci_remap_iospace(res, phys_addr); - if (error) { - devres_free(ptr); - } else { - *ptr = res; - devres_add(dev, ptr); - } - - return error; -} -EXPORT_SYMBOL(devm_pci_remap_iospace); - -/** - * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace() - * @dev: Generic device to remap IO address for - * @offset: Resource address to map - * @size: Size of map - * - * Managed pci_remap_cfgspace(). Map is automatically unmapped on driver - * detach. - */ -void __iomem *devm_pci_remap_cfgspace(struct device *dev, - resource_size_t offset, - resource_size_t size) -{ - void __iomem **ptr, *addr; - - ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - addr = pci_remap_cfgspace(offset, size); - if (addr) { - *ptr = addr; - devres_add(dev, ptr); - } else - devres_free(ptr); - - return addr; -} -EXPORT_SYMBOL(devm_pci_remap_cfgspace); - -/** - * devm_pci_remap_cfg_resource - check, request region and ioremap cfg resource - * @dev: generic device to handle the resource for - * @res: configuration space resource to be handled - * - * Checks that a resource is a valid memory region, requests the memory - * region and ioremaps with pci_remap_cfgspace() API that ensures the - * proper PCI configuration space memory attributes are guaranteed. - * - * 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:: - * - * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - * base = devm_pci_remap_cfg_resource(&pdev->dev, res); - * if (IS_ERR(base)) - * return PTR_ERR(base); - */ -void __iomem *devm_pci_remap_cfg_resource(struct device *dev, - struct resource *res) -{ - 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\n"); - return IOMEM_ERR_PTR(-EINVAL); - } - - size = resource_size(res); - - if (res->name) - name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", dev_name(dev), - res->name); - else - name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); - if (!name) - return IOMEM_ERR_PTR(-ENOMEM); - - if (!devm_request_mem_region(dev, res->start, size, name)) { - dev_err(dev, "can't request region for resource %pR\n", res); - return IOMEM_ERR_PTR(-EBUSY); - } - - dest_ptr = devm_pci_remap_cfgspace(dev, res->start, size); - if (!dest_ptr) { - dev_err(dev, "ioremap failed for resource %pR\n", res); - devm_release_mem_region(dev, res->start, size); - dest_ptr = IOMEM_ERR_PTR(-ENOMEM); - } - - return dest_ptr; -} -EXPORT_SYMBOL(devm_pci_remap_cfg_resource); - static void __pci_set_master(struct pci_dev *dev, bool enable) { u16 old_cmd, cmd; @@ -4629,27 +4401,6 @@ int pci_set_mwi(struct pci_dev *dev) EXPORT_SYMBOL(pci_set_mwi); /** - * pcim_set_mwi - a device-managed pci_set_mwi() - * @dev: the PCI device for which MWI is enabled - * - * Managed pci_set_mwi(). - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -int pcim_set_mwi(struct pci_dev *dev) -{ - struct pci_devres *dr; - - dr = find_pci_dr(dev); - if (!dr) - return -ENOMEM; - - dr->mwi = 1; - return pci_set_mwi(dev); -} -EXPORT_SYMBOL(pcim_set_mwi); - -/** * pci_try_set_mwi - enables memory-write-invalidate PCI transaction * @dev: the PCI device for which MWI is enabled * diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2336a8d1edab..f70066e67d52 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -798,6 +798,27 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) #endif /* + * Managed PCI resources. This manages device on/off, INTx/MSI/MSI-X + * on/off and BAR regions. pci_dev itself records MSI/MSI-X status, so + * there's no need to track it separately. pci_devres is initialized + * when a device is enabled using managed PCI device enable interface. + * + * TODO: Struct pci_devres and find_pci_dr() only need to be here because + * they're used in pci.c. Port or move these functions to devres.c and + * then remove them from here. + */ +struct pci_devres { + unsigned int enabled:1; + unsigned int pinned:1; + unsigned int orig_intx:1; + unsigned int restore_intx:1; + unsigned int mwi:1; + u32 region_mask; +}; + +struct pci_devres *find_pci_dr(struct pci_dev *pdev); + +/* * Config Address for PCI Configuration Mechanism #1 * * See PCI Local Bus Specification, Revision 3.0, |