1394 lines · 35.0 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/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 "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 */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); #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. */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 */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_wc - write-combined variant of *                                     devm_platform_ioremap_resource() * * @pdev: platform device to use both for memory resource lookup as well as *        resource management * @index: resource index */void __iomem *devm_platform_ioremap_resource_wc(struct platform_device *pdev,						unsigned int index){	struct resource *res; 	res = platform_get_resource(pdev, IORESOURCE_MEM, index);	return devm_ioremap_resource_wc(&pdev->dev, res);} /** * 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 */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)		return -ENXIO;	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) {			ret = -ENXIO;			goto out;		}		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;	} 	ret = -ENXIO;#endifout:	WARN(ret == 0, "0 is an invalid IRQ number\n");	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 && ret != -EPROBE_DEFER)		dev_err(&dev->dev, "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); /** * 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 (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {		ret = of_irq_get_byname(dev->dev.of_node, name);		if (ret > 0 || ret == -EPROBE_DEFER)			return ret;	} 	r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);	if (r) {		WARN(r->start == 0, "0 is an invalid IRQ number\n");		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 && ret != -EPROBE_DEFER)		dev_err(&dev->dev, "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 * @num: number of platform devices in array */int platform_add_devices(struct platform_device **devs, int num){	int i, ret = 0; 	for (i = 0; i < num; i++) {		ret = platform_device_register(devs[i]);		if (ret) {			while (--i >= 0)				platform_device_unregister(devs[i]);			break;		}	} 	return ret;}EXPORT_SYMBOL_GPL(platform_add_devices); struct platform_object {	struct platform_device pdev;	char name[];}; /* * Set up default DMA mask for platform devices if the they weren't * previously set by the architecture / DT. */static void setup_pdev_dma_masks(struct platform_device *pdev){	pdev->dev.dma_parms = &pdev->dma_parms; 	if (!pdev->dev.coherent_dma_mask)		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);	if (!pdev->dev.dma_mask) {		pdev->platform_dma_mask = DMA_BIT_MASK(32);		pdev->dev.dma_mask = &pdev->platform_dma_mask;	}}; /** * platform_device_put - destroy a platform device * @pdev: platform device to free * * Free all memory associated with a platform device.  This function must * _only_ be externally called in error cases.  All other usage is a bug. */void platform_device_put(struct platform_device *pdev){	if (!IS_ERR_OR_NULL(pdev))		put_device(&pdev->dev);}EXPORT_SYMBOL_GPL(platform_device_put); static void platform_device_release(struct device *dev){	struct platform_object *pa = container_of(dev, struct platform_object,						  pdev.dev); 	of_device_node_put(&pa->pdev.dev);	kfree(pa->pdev.dev.platform_data);	kfree(pa->pdev.mfd_cell);	kfree(pa->pdev.resource);	kfree(pa->pdev.driver_override);	kfree(pa);} /** * platform_device_alloc - create a platform device * @name: base name of the device we're adding * @id: instance id * * Create a platform device object which can have other objects attached * to it, and which will have attached objects freed when it is released. */struct platform_device *platform_device_alloc(const char *name, int id){	struct platform_object *pa; 	pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL);	if (pa) {		strcpy(pa->name, name);		pa->pdev.name = pa->name;		pa->pdev.id = id;		device_initialize(&pa->pdev.dev);		pa->pdev.dev.release = platform_device_release;		setup_pdev_dma_masks(&pa->pdev);	} 	return pa ? &pa->pdev : NULL;}EXPORT_SYMBOL_GPL(platform_device_alloc); /** * platform_device_add_resources - add resources to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @res: set of resources that needs to be allocated for the device * @num: number of resources * * Add a copy of the resources to the platform device.  The memory * associated with the resources will be freed when the platform device is * released. */int platform_device_add_resources(struct platform_device *pdev,				  const struct resource *res, unsigned int num){	struct resource *r = NULL; 	if (res) {		r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL);		if (!r)			return -ENOMEM;	} 	kfree(pdev->resource);	pdev->resource = r;	pdev->num_resources = num;	return 0;}EXPORT_SYMBOL_GPL(platform_device_add_resources); /** * platform_device_add_data - add platform-specific data to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @data: platform specific data for this platform device * @size: size of platform specific data * * Add a copy of platform specific data to the platform device's * platform_data pointer.  The memory associated with the platform data * will be freed when the platform device is released. */int platform_device_add_data(struct platform_device *pdev, const void *data,			     size_t size){	void *d = NULL; 

Lines 1–500 of 1394.