1527 lines · 37.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * platform.c - platform 'pseudo' bus for legacy devices * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * * Please see Documentation/driver-api/driver-model/platform.rst for more * information. */ #include <linux/string.h>#include <linux/platform_device.h>#include <linux/of_device.h>#include <linux/of_irq.h>#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/dma-mapping.h>#include <linux/memblock.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/pm_runtime.h>#include <linux/pm_domain.h>#include <linux/idr.h>#include <linux/acpi.h>#include <linux/clk/clk-conf.h>#include <linux/limits.h>#include <linux/property.h>#include <linux/kmemleak.h>#include <linux/types.h>#include <linux/iommu.h>#include <linux/dma-map-ops.h> #include "base.h"#include "power/power.h" /* For automatically allocated device IDs */static DEFINE_IDA(platform_devid_ida); struct device platform_bus = {	.init_name	= "platform",};EXPORT_SYMBOL_GPL(platform_bus); /** * platform_get_resource - get a resource for a device * @dev: platform device * @type: resource type * @num: resource index * * Return: a pointer to the resource or NULL on failure. */struct resource *platform_get_resource(struct platform_device *dev,				       unsigned int type, unsigned int num){	u32 i; 	for (i = 0; i < dev->num_resources; i++) {		struct resource *r = &dev->resource[i]; 		if (type == resource_type(r) && num-- == 0)			return r;	}	return NULL;}EXPORT_SYMBOL_GPL(platform_get_resource); struct resource *platform_get_mem_or_io(struct platform_device *dev,					unsigned int num){	u32 i; 	for (i = 0; i < dev->num_resources; i++) {		struct resource *r = &dev->resource[i]; 		if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0)			return r;	}	return NULL;}EXPORT_SYMBOL_GPL(platform_get_mem_or_io); #ifdef CONFIG_HAS_IOMEM/** * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a *					    platform device and get resource * * @pdev: platform device to use both for memory resource lookup as well as *        resource management * @index: resource index * @res: optional output parameter to store a pointer to the obtained resource. * * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code * on failure. */void __iomem *devm_platform_get_and_ioremap_resource(struct platform_device *pdev,				unsigned int index, struct resource **res){	struct resource *r; 	r = platform_get_resource(pdev, IORESOURCE_MEM, index);	if (res)		*res = r;	return devm_ioremap_resource(&pdev->dev, r);}EXPORT_SYMBOL_GPL(devm_platform_get_and_ioremap_resource); /** * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform *				    device * * @pdev: platform device to use both for memory resource lookup as well as *        resource management * @index: resource index * * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code * on failure. */void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,					     unsigned int index){	return devm_platform_get_and_ioremap_resource(pdev, index, NULL);}EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); /** * devm_platform_ioremap_resource_byname - call devm_ioremap_resource for *					   a platform device, retrieve the *					   resource by name * * @pdev: platform device to use both for memory resource lookup as well as *	  resource management * @name: name of the resource * * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code * on failure. */void __iomem *devm_platform_ioremap_resource_byname(struct platform_device *pdev,				      const char *name){	struct resource *res; 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);	return devm_ioremap_resource(&pdev->dev, res);}EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);#endif /* CONFIG_HAS_IOMEM */ /** * platform_get_irq_optional - get an optional IRQ for a device * @dev: platform device * @num: IRQ number index * * Gets an IRQ for a platform device. Device drivers should check the return * value for errors so as to not pass a negative integer value to the * request_irq() APIs. This is the same as platform_get_irq(), except that it * does not print an error message if an IRQ can not be obtained. * * For example:: * *		int irq = platform_get_irq_optional(pdev, 0); *		if (irq < 0) *			return irq; * * Return: non-zero IRQ number on success, negative error number on failure. */int platform_get_irq_optional(struct platform_device *dev, unsigned int num){	int ret;#ifdef CONFIG_SPARC	/* sparc does not have irqs represented as IORESOURCE_IRQ resources */	if (!dev || num >= dev->archdata.num_irqs)		goto out_not_found;	ret = dev->archdata.irqs[num];	goto out;#else	struct resource *r; 	if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {		ret = of_irq_get(dev->dev.of_node, num);		if (ret > 0 || ret == -EPROBE_DEFER)			goto out;	} 	r = platform_get_resource(dev, IORESOURCE_IRQ, num);	if (has_acpi_companion(&dev->dev)) {		if (r && r->flags & IORESOURCE_DISABLED) {			ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);			if (ret)				goto out;		}	} 	/*	 * The resources may pass trigger flags to the irqs that need	 * to be set up. It so happens that the trigger flags for	 * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*	 * settings.	 */	if (r && r->flags & IORESOURCE_BITS) {		struct irq_data *irqd; 		irqd = irq_get_irq_data(r->start);		if (!irqd)			goto out_not_found;		irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);	} 	if (r) {		ret = r->start;		goto out;	} 	/*	 * For the index 0 interrupt, allow falling back to GpioInt	 * resources. While a device could have both Interrupt and GpioInt	 * resources, making this fallback ambiguous, in many common cases	 * the device will only expose one IRQ, and this fallback	 * allows a common code path across either kind of resource.	 */	if (num == 0 && has_acpi_companion(&dev->dev)) {		ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num);		/* Our callers expect -ENXIO for missing IRQs. */		if (ret >= 0 || ret == -EPROBE_DEFER)			goto out;	} #endifout_not_found:	ret = -ENXIO;out:	if (WARN(!ret, "0 is an invalid IRQ number\n"))		return -EINVAL;	return ret;}EXPORT_SYMBOL_GPL(platform_get_irq_optional); /** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index * * Gets an IRQ for a platform device and prints an error message if finding the * IRQ fails. Device drivers should check the return value for errors so as to * not pass a negative integer value to the request_irq() APIs. * * For example:: * *		int irq = platform_get_irq(pdev, 0); *		if (irq < 0) *			return irq; * * Return: non-zero IRQ number on success, negative error number on failure. */int platform_get_irq(struct platform_device *dev, unsigned int num){	int ret; 	ret = platform_get_irq_optional(dev, num);	if (ret < 0)		return dev_err_probe(&dev->dev, ret,				     "IRQ index %u not found\n", num); 	return ret;}EXPORT_SYMBOL_GPL(platform_get_irq); /** * platform_irq_count - Count the number of IRQs a platform device uses * @dev: platform device * * Return: Number of IRQs a platform device uses or EPROBE_DEFER */int platform_irq_count(struct platform_device *dev){	int ret, nr = 0; 	while ((ret = platform_get_irq_optional(dev, nr)) >= 0)		nr++; 	if (ret == -EPROBE_DEFER)		return ret; 	return nr;}EXPORT_SYMBOL_GPL(platform_irq_count); struct irq_affinity_devres {	unsigned int count;	unsigned int irq[];}; static void platform_disable_acpi_irq(struct platform_device *pdev, int index){	struct resource *r; 	r = platform_get_resource(pdev, IORESOURCE_IRQ, index);	if (r)		irqresource_disabled(r, 0);} static void devm_platform_get_irqs_affinity_release(struct device *dev,						    void *res){	struct irq_affinity_devres *ptr = res;	int i; 	for (i = 0; i < ptr->count; i++) {		irq_dispose_mapping(ptr->irq[i]); 		if (has_acpi_companion(dev))			platform_disable_acpi_irq(to_platform_device(dev), i);	}} /** * devm_platform_get_irqs_affinity - devm method to get a set of IRQs for a *				device using an interrupt affinity descriptor * @dev: platform device pointer * @affd: affinity descriptor * @minvec: minimum count of interrupt vectors * @maxvec: maximum count of interrupt vectors * @irqs: pointer holder for IRQ numbers * * Gets a set of IRQs for a platform device, and updates IRQ afffinty according * to the passed affinity descriptor * * Return: Number of vectors on success, negative error number on failure. */int devm_platform_get_irqs_affinity(struct platform_device *dev,				    struct irq_affinity *affd,				    unsigned int minvec,				    unsigned int maxvec,				    int **irqs){	struct irq_affinity_devres *ptr;	struct irq_affinity_desc *desc;	size_t size;	int i, ret, nvec; 	if (!affd)		return -EPERM; 	if (maxvec < minvec)		return -ERANGE; 	nvec = platform_irq_count(dev);	if (nvec < 0)		return nvec; 	if (nvec < minvec)		return -ENOSPC; 	nvec = irq_calc_affinity_vectors(minvec, nvec, affd);	if (nvec < minvec)		return -ENOSPC; 	if (nvec > maxvec)		nvec = maxvec; 	size = sizeof(*ptr) + sizeof(unsigned int) * nvec;	ptr = devres_alloc(devm_platform_get_irqs_affinity_release, size,			   GFP_KERNEL);	if (!ptr)		return -ENOMEM; 	ptr->count = nvec; 	for (i = 0; i < nvec; i++) {		int irq = platform_get_irq(dev, i);		if (irq < 0) {			ret = irq;			goto err_free_devres;		}		ptr->irq[i] = irq;	} 	desc = irq_create_affinity_masks(nvec, affd);	if (!desc) {		ret = -ENOMEM;		goto err_free_devres;	} 	for (i = 0; i < nvec; i++) {		ret = irq_update_affinity_desc(ptr->irq[i], &desc[i]);		if (ret) {			dev_err(&dev->dev, "failed to update irq%d affinity descriptor (%d)\n",				ptr->irq[i], ret);			goto err_free_desc;		}	} 	devres_add(&dev->dev, ptr); 	kfree(desc); 	*irqs = ptr->irq; 	return nvec; err_free_desc:	kfree(desc);err_free_devres:	devres_free(ptr);	return ret;}EXPORT_SYMBOL_GPL(devm_platform_get_irqs_affinity); /** * platform_get_resource_byname - get a resource for a device by name * @dev: platform device * @type: resource type * @name: resource name */struct resource *platform_get_resource_byname(struct platform_device *dev,					      unsigned int type,					      const char *name){	u32 i; 	for (i = 0; i < dev->num_resources; i++) {		struct resource *r = &dev->resource[i]; 		if (unlikely(!r->name))			continue; 		if (type == resource_type(r) && !strcmp(r->name, name))			return r;	}	return NULL;}EXPORT_SYMBOL_GPL(platform_get_resource_byname); static int __platform_get_irq_byname(struct platform_device *dev,				     const char *name){	struct resource *r;	int ret; 	if (!dev->dev.of_node || IS_ENABLED(CONFIG_OF_IRQ)) {		ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name);		if (ret > 0 || ret == -EPROBE_DEFER)			return ret;	} 	r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);	if (r) {		if (WARN(!r->start, "0 is an invalid IRQ number\n"))			return -EINVAL;		return r->start;	} 	return -ENXIO;} /** * platform_get_irq_byname - get an IRQ for a device by name * @dev: platform device * @name: IRQ name * * Get an IRQ like platform_get_irq(), but then by name rather then by index. * * Return: non-zero IRQ number on success, negative error number on failure. */int platform_get_irq_byname(struct platform_device *dev, const char *name){	int ret; 	ret = __platform_get_irq_byname(dev, name);	if (ret < 0)		return dev_err_probe(&dev->dev, ret, "IRQ %s not found\n",				     name);	return ret;}EXPORT_SYMBOL_GPL(platform_get_irq_byname); /** * platform_get_irq_byname_optional - get an optional IRQ for a device by name * @dev: platform device * @name: IRQ name * * Get an optional IRQ by name like platform_get_irq_byname(). Except that it * does not print an error message if an IRQ can not be obtained. * * Return: non-zero IRQ number on success, negative error number on failure. */int platform_get_irq_byname_optional(struct platform_device *dev,				     const char *name){	return __platform_get_irq_byname(dev, name);}EXPORT_SYMBOL_GPL(platform_get_irq_byname_optional); /** * platform_add_devices - add a numbers of platform devices * @devs: array of platform devices to add

Lines 1–500 of 1527.