1597 lines · 40.1 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 "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){	int 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); /** * 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 */#ifdef CONFIG_HAS_IOMEMvoid __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,					     unsigned int index){	struct resource *res; 	res = platform_get_resource(pdev, IORESOURCE_MEM, index);	return devm_ioremap_resource(&pdev->dev, res);}EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource);#endif /* CONFIG_HAS_IOMEM */ static int __platform_get_irq(struct platform_device *dev, unsigned int num){#ifdef CONFIG_SPARC	/* sparc does not have irqs represented as IORESOURCE_IRQ resources */	if (!dev || num >= dev->archdata.num_irqs)		return -ENXIO;	return dev->archdata.irqs[num];#else	struct resource *r;	if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {		int ret; 		ret = of_irq_get(dev->dev.of_node, num);		if (ret > 0 || ret == -EPROBE_DEFER)			return ret;	} 	r = platform_get_resource(dev, IORESOURCE_IRQ, num);	if (has_acpi_companion(&dev->dev)) {		if (r && r->flags & IORESOURCE_DISABLED) {			int ret; 			ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);			if (ret)				return ret;		}	} 	/*	 * 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)			return -ENXIO;		irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);	} 	if (r)		return r->start; 	/*	 * 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)) {		int ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); 		/* Our callers expect -ENXIO for missing IRQs. */		if (ret >= 0 || ret == -EPROBE_DEFER)			return ret;	} 	return -ENXIO;#endif} /** * 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. * * Example: *		int irq = platform_get_irq(pdev, 0); *		if (irq < 0) *			return irq; * * Return: 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(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_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. * * Example: *		int irq = platform_get_irq_optional(pdev, 0); *		if (irq < 0) *			return irq; * * Return: IRQ number on success, negative error number on failure. */int platform_get_irq_optional(struct platform_device *dev, unsigned int num){	return __platform_get_irq(dev, num);}EXPORT_SYMBOL_GPL(platform_get_irq_optional); /** * 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(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){	int 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; 	if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {		int ret; 		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)		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: 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: 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){	if (!pdev->dev.coherent_dma_mask)		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);	if (!pdev->dma_mask)		pdev->dma_mask = DMA_BIT_MASK(32);	if (!pdev->dev.dma_mask)		pdev->dev.dma_mask = &pdev->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; 	if (data) {		d = kmemdup(data, size, GFP_KERNEL);		if (!d)			return -ENOMEM;	} 	kfree(pdev->dev.platform_data);	pdev->dev.platform_data = d;	return 0;}EXPORT_SYMBOL_GPL(platform_device_add_data); /** * platform_device_add_properties - add built-in properties to a platform device * @pdev: platform device to add properties to * @properties: null terminated array of properties to add * * The function will take deep copy of @properties and attach the copy to the * platform device. The memory associated with properties will be freed when the * platform device is released. */int platform_device_add_properties(struct platform_device *pdev,				   const struct property_entry *properties){	return device_add_properties(&pdev->dev, properties);}EXPORT_SYMBOL_GPL(platform_device_add_properties); /** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */int platform_device_add(struct platform_device *pdev){	int i, ret; 	if (!pdev)		return -EINVAL; 	if (!pdev->dev.parent)		pdev->dev.parent = &platform_bus; 	pdev->dev.bus = &platform_bus_type; 	switch (pdev->id) {	default:		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);		break;	case PLATFORM_DEVID_NONE:		dev_set_name(&pdev->dev, "%s", pdev->name);		break;	case PLATFORM_DEVID_AUTO:		/*		 * Automatically allocated device ID. We mark it as such so		 * that we remember it must be freed, and we append a suffix		 * to avoid namespace collision with explicit IDs.		 */		ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);		if (ret < 0)

Lines 1–500 of 1597.