1629 lines · 42.3 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 */ static const struct cpumask *get_irq_affinity(struct platform_device *dev,					      unsigned int num){	const struct cpumask *mask = NULL;#ifndef CONFIG_SPARC	struct fwnode_handle *fwnode = dev_fwnode(&dev->dev); 	if (is_of_node(fwnode))		mask = of_irq_get_affinity(to_of_node(fwnode), num);	else if (is_acpi_device_node(fwnode))		mask = acpi_irq_get_affinity(ACPI_HANDLE_FWNODE(fwnode), num);#endif 	return mask ?: cpu_possible_mask;} /** * platform_get_irq_affinity - get an optional IRQ and its affinity for a device * @dev:	platform device * @num:	interrupt number index * @affinity:	optional cpumask pointer to get the affinity of a per-cpu interrupt * * Gets an interrupt 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. Optional affinity information is provided in the * affinity pointer if available, and NULL otherwise. * * Return: non-zero interrupt number on success, negative error number on failure. */int platform_get_irq_affinity(struct platform_device *dev, unsigned int num,			      const struct cpumask **affinity){	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 fwnode_handle *fwnode = dev_fwnode(&dev->dev);	struct resource *r; 	if (is_of_node(fwnode)) {		ret = of_irq_get(to_of_node(fwnode), num);		if (ret > 0 || ret == -EPROBE_DEFER)			goto out;	} 	r = platform_get_resource(dev, IORESOURCE_IRQ, num);	if (is_acpi_device_node(fwnode)) {		if (r && r->flags & IORESOURCE_DISABLED) {			ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), 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 && is_acpi_device_node(fwnode)) {		ret = acpi_dev_gpio_irq_get(to_acpi_device_node(fwnode), 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; 	if (ret > 0 && affinity)		*affinity = get_irq_affinity(dev, num); 	return ret;}EXPORT_SYMBOL_GPL(platform_get_irq_affinity); /** * platform_get_irq_optional - get an optional interrupt for a device * @dev:	platform device * @num:	interrupt number index * * Gets an interrupt 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 interrupt can not be * obtained. * * For example:: * *		int irq = platform_get_irq_optional(pdev, 0); *		if (irq < 0) *			return irq; * * Return: non-zero interrupt number on success, negative error number on failure. */int platform_get_irq_optional(struct platform_device *dev, unsigned int num){	return platform_get_irq_affinity(dev, num, NULL);}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[] __counted_by(count);}; 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 (is_acpi_device_node(dev_fwnode(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; 	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;} /**

Lines 1–500 of 1629.