1502 lines · 37.5 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-model/platform.txt 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); /** * arch_setup_pdev_archdata - Allow manipulation of archdata before its used * @pdev: platform device * * This is called before platform_device_add() such that any pdev_archdata may * be setup before the platform_notifier is called.  So if a user needs to * manipulate any relevant information in the pdev_archdata they can do: * *	platform_device_alloc() *	... manipulate ... *	platform_device_add() * * And if they don't care they can just call platform_device_register() and * everything will just work out. */void __weak arch_setup_pdev_archdata(struct platform_device *pdev){} /** * 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 */ /** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index */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))		return acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); 	return -ENXIO;#endif}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(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); /** * platform_get_irq_byname - get an IRQ for a device by name * @dev: platform device * @name: IRQ name */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);	return r ? r->start : -ENXIO;}EXPORT_SYMBOL_GPL(platform_get_irq_byname); /** * 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[];}; /** * 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;		arch_setup_pdev_archdata(&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)			goto err_out;		pdev->id = ret;		pdev->id_auto = true;		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);		break;	} 	for (i = 0; i < pdev->num_resources; i++) {		struct resource *p, *r = &pdev->resource[i]; 		if (r->name == NULL)			r->name = dev_name(&pdev->dev); 		p = r->parent;		if (!p) {			if (resource_type(r) == IORESOURCE_MEM)				p = &iomem_resource;			else if (resource_type(r) == IORESOURCE_IO)				p = &ioport_resource;		} 		if (p) {			ret = insert_resource(p, r);			if (ret) {				dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);				goto failed;			}		}	} 	pr_debug("Registering platform device '%s'. Parent at %s\n",		 dev_name(&pdev->dev), dev_name(pdev->dev.parent)); 	ret = device_add(&pdev->dev);	if (ret == 0)		return ret;  failed:	if (pdev->id_auto) {		ida_simple_remove(&platform_devid_ida, pdev->id);		pdev->id = PLATFORM_DEVID_AUTO;	} 	while (--i >= 0) {		struct resource *r = &pdev->resource[i];		if (r->parent)			release_resource(r);	}  err_out:	return ret;}EXPORT_SYMBOL_GPL(platform_device_add); /** * platform_device_del - remove a platform-level device * @pdev: platform device we're removing * * Note that this function will also release all memory- and port-based * resources owned by the device (@dev->resource).  This function must * _only_ be externally called in error cases.  All other usage is a bug. */void platform_device_del(struct platform_device *pdev){	int i; 	if (!IS_ERR_OR_NULL(pdev)) {		device_del(&pdev->dev); 		if (pdev->id_auto) {			ida_simple_remove(&platform_devid_ida, pdev->id);			pdev->id = PLATFORM_DEVID_AUTO;		} 		for (i = 0; i < pdev->num_resources; i++) {			struct resource *r = &pdev->resource[i];			if (r->parent)				release_resource(r);		}	}}

Lines 1–500 of 1502.