861 lines · 20.8 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 PCI devres API has one significant problem: * * 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. * * 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_NUM_RESOURCES];}; /* 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;} 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:		pci_release_region(pdev, res->bar);		break;	case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING:		pci_iounmap(pdev, res->baseaddr);		pci_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);} static void pcim_intx_restore(struct device *dev, void *data){	struct pci_dev *pdev = to_pci_dev(dev);	struct pcim_intx_devres *res = data; 	pci_intx(pdev, res->orig_intx);} static void save_orig_intx(struct pci_dev *pdev, struct pcim_intx_devres *res){	u16 pci_command; 	pci_read_config_word(pdev, PCI_COMMAND, &pci_command);	res->orig_intx = !(pci_command & PCI_COMMAND_INTX_DISABLE);} /** * 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;	struct device *dev = &pdev->dev; 	/*	 * pcim_intx() must only restore the INTx value that existed before the	 * driver was loaded, i.e., before it called pcim_intx() for the	 * first time.	 */	res = devres_find(dev, pcim_intx_restore, NULL, NULL);	if (!res) {		res = devres_alloc(pcim_intx_restore, sizeof(*res), GFP_KERNEL);		if (!res)			return -ENOMEM; 		save_orig_intx(pdev, res);		devres_add(dev, res);	} 	pci_intx(pdev, enable); 	return 0;}EXPORT_SYMBOL_GPL(pcim_intx); 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){	int ret; 	ret = devm_add_action(&pdev->dev, pcim_disable_device, pdev);	if (ret != 0)		return ret; 	/*	 * We prefer removing the action in case of an error over	 * devm_add_action_or_reset() because the latter could theoretically be	 * disturbed by users having pinned the device too soon.	 */	ret = pci_enable_device(pdev);	if (ret != 0) {		devm_remove_action(&pdev->dev, pcim_disable_device, pdev);		return ret;	} 	pdev->is_managed = true; 	return ret;}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){	pdev->pinned = true;}EXPORT_SYMBOL(pcim_pin_device); static void pcim_iomap_release(struct device *gendev, void *res){	/*	 * Do nothing. This is legacy code.	 *	 * Cleanup of the mappings is now done directly through the callbacks	 * registered when creating them.	 */} /** * pcim_iomap_table - access iomap allocation table (DEPRECATED) * @pdev: PCI device to access iomap table for * * Returns: * Const pointer to array of __iomem pointers on success, NULL on failure. * * Access iomap allocation table for @dev.  If iomap table doesn't * exist and @pdev is managed, it will be allocated.  All iomaps * recorded in the iomap table are automatically unmapped on driver * detach. * * This function might sleep when the table is first allocated but can * be safely called without context and guaranteed to succeed once * allocated. * * This function is DEPRECATED. Do not use it in new code. Instead, obtain a * mapping's address directly from one of the pcim_* mapping functions. For * example: * void __iomem \*mappy = pcim_iomap(pdev, bar, length); */void __iomem * const *pcim_iomap_table(struct pci_dev *pdev){	struct pcim_iomap_devres *dr, *new_dr; 	dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL);	if (dr)		return dr->table; 	new_dr = devres_alloc_node(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL,				   dev_to_node(&pdev->dev));	if (!new_dr)		return NULL;	dr = devres_get(&pdev->dev, new_dr, NULL, NULL);	return dr->table;}EXPORT_SYMBOL(pcim_iomap_table); /* * Fill the legacy mapping-table, so that drivers using the old API can * still get a BAR's mapping address through pcim_iomap_table(). */static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev,					    void __iomem *mapping, int bar){	void __iomem **legacy_iomap_table; 	if (!pci_bar_index_is_valid(bar))		return -EINVAL; 	legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);	if (!legacy_iomap_table)		return -ENOMEM; 	legacy_iomap_table[bar] = mapping; 	return 0;} /* * Remove a mapping. The table only contains whole-BAR mappings, so this will * never interfere with ranged mappings. */static void pcim_remove_mapping_from_legacy_table(struct pci_dev *pdev,						  void __iomem *addr){	int bar;	void __iomem **legacy_iomap_table; 	legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);	if (!legacy_iomap_table)		return; 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {		if (legacy_iomap_table[bar] == addr) {			legacy_iomap_table[bar] = NULL;			return;		}	}} /* * The same as pcim_remove_mapping_from_legacy_table(), but identifies the

Lines 1–500 of 861.