1029 lines · 25.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/device.h>#include <linux/pci.h>#include "pci.h" /* * On the state of PCI's devres implementation: * * The older devres API for PCI has two significant problems: * * 1. It is very strongly tied to the statically allocated mapping table in *    struct pcim_iomap_devres below. This is mostly solved in the sense of the *    pcim_ functions in this file providing things like ranged mapping by *    bypassing this table, whereas the functions that were present in the old *    API still enter the mapping addresses into the table for users of the old *    API. * * 2. The region-request-functions in pci.c do become managed IF the device has *    been enabled with pcim_enable_device() instead of pci_enable_device(). *    This resulted in the API becoming inconsistent: Some functions have an *    obviously managed counter-part (e.g., pci_iomap() <-> pcim_iomap()), *    whereas some don't and are never managed, while others don't and are *    _sometimes_ managed (e.g. pci_request_region()). * *    Consequently, in the new API, region requests performed by the pcim_ *    functions are automatically cleaned up through the devres callback *    pcim_addr_resource_release(). * *    Users of pcim_enable_device() + pci_*region*() are redirected in *    pci.c to the managed functions here in this file. This isn't exactly *    perfect, but the only alternative way would be to port ALL drivers *    using said combination to pcim_ functions. * * TODO: * Remove the legacy table entirely once all calls to pcim_iomap_table() in * the kernel have been removed. */ /* * Legacy struct storing addresses to whole mapped BARs. */struct pcim_iomap_devres {	void __iomem *table[PCI_STD_NUM_BARS];}; /* Used to restore the old INTx state on driver detach. */struct pcim_intx_devres {	int orig_intx;}; enum pcim_addr_devres_type {	/* Default initializer. */	PCIM_ADDR_DEVRES_TYPE_INVALID, 	/* A requested region spanning an entire BAR. */	PCIM_ADDR_DEVRES_TYPE_REGION, 	/*	 * A requested region spanning an entire BAR, and a mapping for	 * the entire BAR.	 */	PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING, 	/*	 * A mapping within a BAR, either spanning the whole BAR or just a	 * range.  Without a requested region.	 */	PCIM_ADDR_DEVRES_TYPE_MAPPING,}; /* * This struct envelops IO or MEM addresses, i.e., mappings and region * requests, because those are very frequently requested and released * together. */struct pcim_addr_devres {	enum pcim_addr_devres_type type;	void __iomem *baseaddr;	unsigned long offset;	unsigned long len;	int bar;}; static inline void pcim_addr_devres_clear(struct pcim_addr_devres *res){	memset(res, 0, sizeof(*res));	res->bar = -1;} /* * The following functions, __pcim_*_region*, exist as counterparts to the * versions from pci.c - which, unfortunately, can be in "hybrid mode", i.e., * sometimes managed, sometimes not. * * To separate the APIs cleanly, we define our own, simplified versions here. */ /** * __pcim_request_region_range - Request a ranged region * @pdev: PCI device the region belongs to * @bar: BAR the range is within * @offset: offset from the BAR's start address * @maxlen: length in bytes, beginning at @offset * @name: name associated with the request * @req_flags: flags for the request, e.g., for kernel-exclusive requests * * Returns: 0 on success, a negative error code on failure. * * Request a range within a device's PCI BAR.  Sanity check the input. */static int __pcim_request_region_range(struct pci_dev *pdev, int bar,				       unsigned long offset,				       unsigned long maxlen,				       const char *name, int req_flags){	resource_size_t start = pci_resource_start(pdev, bar);	resource_size_t len = pci_resource_len(pdev, bar);	unsigned long dev_flags = pci_resource_flags(pdev, bar); 	if (start == 0 || len == 0) /* Unused BAR. */		return 0;	if (len <= offset)		return -EINVAL; 	start += offset;	len -= offset; 	if (len > maxlen && maxlen != 0)		len = maxlen; 	if (dev_flags & IORESOURCE_IO) {		if (!request_region(start, len, name))			return -EBUSY;	} else if (dev_flags & IORESOURCE_MEM) {		if (!__request_mem_region(start, len, name, req_flags))			return -EBUSY;	} else {		/* That's not a device we can request anything on. */		return -ENODEV;	} 	return 0;} static void __pcim_release_region_range(struct pci_dev *pdev, int bar,					unsigned long offset,					unsigned long maxlen){	resource_size_t start = pci_resource_start(pdev, bar);	resource_size_t len = pci_resource_len(pdev, bar);	unsigned long flags = pci_resource_flags(pdev, bar); 	if (len <= offset || start == 0)		return; 	if (len == 0 || maxlen == 0) /* This an unused BAR. Do nothing. */		return; 	start += offset;	len -= offset; 	if (len > maxlen)		len = maxlen; 	if (flags & IORESOURCE_IO)		release_region(start, len);	else if (flags & IORESOURCE_MEM)		release_mem_region(start, len);} static int __pcim_request_region(struct pci_dev *pdev, int bar,				 const char *name, int flags){	unsigned long offset = 0;	unsigned long len = pci_resource_len(pdev, bar); 	return __pcim_request_region_range(pdev, bar, offset, len, name, flags);} static void __pcim_release_region(struct pci_dev *pdev, int bar){	unsigned long offset = 0;	unsigned long len = pci_resource_len(pdev, bar); 	__pcim_release_region_range(pdev, bar, offset, len);} static void pcim_addr_resource_release(struct device *dev, void *resource_raw){	struct pci_dev *pdev = to_pci_dev(dev);	struct pcim_addr_devres *res = resource_raw; 	switch (res->type) {	case PCIM_ADDR_DEVRES_TYPE_REGION:		__pcim_release_region(pdev, res->bar);		break;	case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING:		pci_iounmap(pdev, res->baseaddr);		__pcim_release_region(pdev, res->bar);		break;	case PCIM_ADDR_DEVRES_TYPE_MAPPING:		pci_iounmap(pdev, res->baseaddr);		break;	default:		break;	}} static struct pcim_addr_devres *pcim_addr_devres_alloc(struct pci_dev *pdev){	struct pcim_addr_devres *res; 	res = devres_alloc_node(pcim_addr_resource_release, sizeof(*res),				GFP_KERNEL, dev_to_node(&pdev->dev));	if (res)		pcim_addr_devres_clear(res);	return res;} /* Just for consistency and readability. */static inline void pcim_addr_devres_free(struct pcim_addr_devres *res){	devres_free(res);} /* * Used by devres to identify a pcim_addr_devres. */static int pcim_addr_resources_match(struct device *dev,				     void *a_raw, void *b_raw){	struct pcim_addr_devres *a, *b; 	a = a_raw;	b = b_raw; 	if (a->type != b->type)		return 0; 	switch (a->type) {	case PCIM_ADDR_DEVRES_TYPE_REGION:	case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING:		return a->bar == b->bar;	case PCIM_ADDR_DEVRES_TYPE_MAPPING:		return a->baseaddr == b->baseaddr;	default:		return 0;	}} 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 IOMEM_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 __pcim_clear_mwi(void *pdev_raw){	struct pci_dev *pdev = pdev_raw; 	pci_clear_mwi(pdev);} /** * pcim_set_mwi - a device-managed pci_set_mwi() * @pdev: 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 *pdev){	int ret; 	ret = devm_add_action(&pdev->dev, __pcim_clear_mwi, pdev);	if (ret != 0)		return ret; 	ret = pci_set_mwi(pdev);	if (ret != 0)		devm_remove_action(&pdev->dev, __pcim_clear_mwi, pdev); 	return ret;}EXPORT_SYMBOL(pcim_set_mwi); static inline bool mask_contains_bar(int mask, int bar){	return mask & BIT(bar);} /* * This is a copy of pci_intx() used to bypass the problem of recursive * function calls due to the hybrid nature of pci_intx(). */static void __pcim_intx(struct pci_dev *pdev, int enable){	u16 pci_command, new; 	pci_read_config_word(pdev, PCI_COMMAND, &pci_command); 	if (enable)		new = pci_command & ~PCI_COMMAND_INTX_DISABLE;	else		new = pci_command | PCI_COMMAND_INTX_DISABLE; 	if (new != pci_command)		pci_write_config_word(pdev, PCI_COMMAND, new);} static void pcim_intx_restore(struct device *dev, void *data){	struct pci_dev *pdev = to_pci_dev(dev);	struct pcim_intx_devres *res = data; 	__pcim_intx(pdev, res->orig_intx);} static struct pcim_intx_devres *get_or_create_intx_devres(struct device *dev){	struct pcim_intx_devres *res; 	res = devres_find(dev, pcim_intx_restore, NULL, NULL);	if (res)		return res; 	res = devres_alloc(pcim_intx_restore, sizeof(*res), GFP_KERNEL);	if (res)		devres_add(dev, res); 	return res;} /** * pcim_intx - managed pci_intx() * @pdev: the PCI device to operate on * @enable: boolean: whether to enable or disable PCI INTx * * Returns: 0 on success, -ENOMEM on error. * * Enable/disable PCI INTx for device @pdev. * Restore the original state on driver detach. */int pcim_intx(struct pci_dev *pdev, int enable){	struct pcim_intx_devres *res; 	res = get_or_create_intx_devres(&pdev->dev);	if (!res)		return -ENOMEM; 	res->orig_intx = !enable;	__pcim_intx(pdev, enable); 	return 0;} static void pcim_disable_device(void *pdev_raw){	struct pci_dev *pdev = pdev_raw; 	if (!pdev->pinned)		pci_disable_device(pdev); 	pdev->is_managed = false;} /** * pcim_enable_device - Managed pci_enable_device() * @pdev: PCI device to be initialized * * Returns: 0 on success, negative error code on failure. * * Managed pci_enable_device(). Device will automatically be disabled on * driver detach. */int pcim_enable_device(struct pci_dev *pdev){

Lines 1–500 of 1029.