1226 lines · 30.4 KB · C
RawDownload
/* * platform.c - platform 'pseudo' bus for legacy devices * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * * This file is released under the GPLv2 * * 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/module.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/bootmem.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/pm_runtime.h>#include <linux/idr.h>#include <linux/acpi.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); /** * 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 = platform_get_resource(dev, IORESOURCE_IRQ, num); 	return r ? r->start : -ENXIO;#endif}EXPORT_SYMBOL_GPL(platform_get_irq); /** * 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 = 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[1];}; /** * 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 (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);} /** * 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(struct platform_object) + strlen(name), 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 - 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 && insert_resource(p, r)) {			dev_err(&pdev->dev, "failed to claim resource %d\n", i);			ret = -EBUSY;			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];		unsigned long type = resource_type(r); 		if (type == IORESOURCE_MEM || type == IORESOURCE_IO)			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 (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];			unsigned long type = resource_type(r); 			if (type == IORESOURCE_MEM || type == IORESOURCE_IO)				release_resource(r);		}	}}EXPORT_SYMBOL_GPL(platform_device_del); /** * platform_device_register - add a platform-level device * @pdev: platform device we're adding */int platform_device_register(struct platform_device *pdev){	device_initialize(&pdev->dev);	arch_setup_pdev_archdata(pdev);	return platform_device_add(pdev);}EXPORT_SYMBOL_GPL(platform_device_register); /** * platform_device_unregister - unregister a platform-level device * @pdev: platform device we're unregistering * * Unregistration is done in 2 steps. First we release all resources * and remove it from the subsystem, then we drop reference count by * calling platform_device_put(). */void platform_device_unregister(struct platform_device *pdev){	platform_device_del(pdev);	platform_device_put(pdev);}EXPORT_SYMBOL_GPL(platform_device_unregister); /** * platform_device_register_full - add a platform-level device with * resources and platform-specific data * * @pdevinfo: data used to create device * * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */struct platform_device *platform_device_register_full(		const struct platform_device_info *pdevinfo){	int ret = -ENOMEM;	struct platform_device *pdev; 	pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);	if (!pdev)		goto err_alloc; 	pdev->dev.parent = pdevinfo->parent;	ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle); 	if (pdevinfo->dma_mask) {		/*		 * This memory isn't freed when the device is put,		 * I don't have a nice idea for that though.  Conceptually		 * dma_mask in struct device should not be a pointer.		 * See http://thread.gmane.org/gmane.linux.kernel.pci/9081		 */		pdev->dev.dma_mask =			kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);		if (!pdev->dev.dma_mask)			goto err; 		*pdev->dev.dma_mask = pdevinfo->dma_mask;		pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;	} 	ret = platform_device_add_resources(pdev,			pdevinfo->res, pdevinfo->num_res);	if (ret)		goto err; 	ret = platform_device_add_data(pdev,			pdevinfo->data, pdevinfo->size_data);	if (ret)		goto err; 	ret = platform_device_add(pdev);	if (ret) {err:		ACPI_HANDLE_SET(&pdev->dev, NULL);		kfree(pdev->dev.dma_mask); err_alloc:		platform_device_put(pdev);		return ERR_PTR(ret);	} 	return pdev;}EXPORT_SYMBOL_GPL(platform_device_register_full); static int platform_drv_probe(struct device *_dev){	struct platform_driver *drv = to_platform_driver(_dev->driver);	struct platform_device *dev = to_platform_device(_dev);	int ret; 	if (ACPI_HANDLE(_dev))		acpi_dev_pm_attach(_dev, true); 	ret = drv->probe(dev);	if (ret && ACPI_HANDLE(_dev))		acpi_dev_pm_detach(_dev, true); 	return ret;} static int platform_drv_probe_fail(struct device *_dev){	return -ENXIO;} static int platform_drv_remove(struct device *_dev){

Lines 1–500 of 1226.