diff options
Diffstat (limited to 'drivers/base/platform.c')
| -rw-r--r-- | drivers/base/platform.c | 687 |
1 files changed, 354 insertions, 333 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 58efaf2f125..eee48c49f5d 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -12,6 +12,8 @@ #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> @@ -19,11 +21,14 @@ #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" -#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ - driver)) +/* For automatically allocated device IDs */ +static DEFINE_IDA(platform_devid_ida); struct device platform_bus = { .init_name = "platform", @@ -31,6 +36,25 @@ struct device platform_bus = { 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 @@ -58,9 +82,25 @@ EXPORT_SYMBOL_GPL(platform_get_resource); */ int platform_get_irq(struct platform_device *dev, unsigned int num) { - struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, 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); return r ? r->start : -ENXIO; +#endif } EXPORT_SYMBOL_GPL(platform_get_irq); @@ -79,6 +119,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, 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; } @@ -87,15 +130,23 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, EXPORT_SYMBOL_GPL(platform_get_resource_byname); /** - * platform_get_irq - get an IRQ for a device + * 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); + 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); @@ -128,7 +179,7 @@ struct platform_object { }; /** - * platform_device_put + * platform_device_put - destroy a platform device * @pdev: platform device to free * * Free all memory associated with a platform device. This function must @@ -146,13 +197,15 @@ 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 + * platform_device_alloc - create a platform device * @name: base name of the device we're adding * @id: instance id * @@ -170,6 +223,7 @@ struct platform_device *platform_device_alloc(const char *name, int id) 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; @@ -177,7 +231,7 @@ struct platform_device *platform_device_alloc(const char *name, int id) EXPORT_SYMBOL_GPL(platform_device_alloc); /** - * platform_device_add_resources + * 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 @@ -187,22 +241,25 @@ EXPORT_SYMBOL_GPL(platform_device_alloc); * released. */ int platform_device_add_resources(struct platform_device *pdev, - struct resource *res, unsigned int num) + const struct resource *res, unsigned int num) { - struct resource *r; + struct resource *r = NULL; - r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL); - if (r) { - memcpy(r, res, sizeof(struct resource) * num); - pdev->resource = r; - pdev->num_resources = num; + if (res) { + r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); + if (!r) + return -ENOMEM; } - return r ? 0 : -ENOMEM; + + kfree(pdev->resource); + pdev->resource = r; + pdev->num_resources = num; + return 0; } EXPORT_SYMBOL_GPL(platform_device_add_resources); /** - * platform_device_add_data + * 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 @@ -214,13 +271,17 @@ EXPORT_SYMBOL_GPL(platform_device_add_resources); int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size) { - void *d = kmemdup(data, size, GFP_KERNEL); + void *d = NULL; - if (d) { - pdev->dev.platform_data = d; - return 0; + if (data) { + d = kmemdup(data, size, GFP_KERNEL); + if (!d) + return -ENOMEM; } - return -ENOMEM; + + kfree(pdev->dev.platform_data); + pdev->dev.platform_data = d; + return 0; } EXPORT_SYMBOL_GPL(platform_device_add_data); @@ -233,7 +294,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data); */ int platform_device_add(struct platform_device *pdev) { - int i, ret = 0; + int i, ret; if (!pdev) return -EINVAL; @@ -243,10 +304,27 @@ int platform_device_add(struct platform_device *pdev) pdev->dev.bus = &platform_bus_type; - if (pdev->id != -1) + switch (pdev->id) { + default: dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); - else + 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]; @@ -263,9 +341,7 @@ int platform_device_add(struct platform_device *pdev) } if (p && insert_resource(p, r)) { - printk(KERN_ERR - "%s: failed to claim resource %d\n", - dev_name(&pdev->dev), i); + dev_err(&pdev->dev, "failed to claim resource %d\n", i); ret = -EBUSY; goto failed; } @@ -279,6 +355,11 @@ int platform_device_add(struct platform_device *pdev) 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); @@ -287,6 +368,7 @@ int platform_device_add(struct platform_device *pdev) release_resource(r); } + err_out: return ret; } EXPORT_SYMBOL_GPL(platform_device_add); @@ -306,6 +388,11 @@ void platform_device_del(struct platform_device *pdev) 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); @@ -324,6 +411,7 @@ EXPORT_SYMBOL_GPL(platform_device_del); 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); @@ -344,111 +432,85 @@ void platform_device_unregister(struct platform_device *pdev) EXPORT_SYMBOL_GPL(platform_device_unregister); /** - * platform_device_register_simple - * @name: base name of the device we're adding - * @id: instance id - * @res: set of resources that needs to be allocated for the device - * @num: number of resources + * platform_device_register_full - add a platform-level device with + * resources and platform-specific data * - * This function creates a simple platform device that requires minimal - * resource and memory management. Canned release function freeing memory - * allocated for the device allows drivers using such devices to be - * unloaded without waiting for the last reference to the device to be - * dropped. + * @pdevinfo: data used to create device * - * This interface is primarily intended for use with legacy drivers which - * probe hardware directly. Because such drivers create sysfs device nodes - * themselves, rather than letting system infrastructure handle such device - * enumeration tasks, they don't fully conform to the Linux driver model. - * In particular, when such drivers are built as modules, they can't be - * "hotplugged". + * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */ -struct platform_device *platform_device_register_simple(const char *name, - int id, - struct resource *res, - unsigned int num) +struct platform_device *platform_device_register_full( + const struct platform_device_info *pdevinfo) { + int ret = -ENOMEM; struct platform_device *pdev; - int retval; - pdev = platform_device_alloc(name, id); - if (!pdev) { - retval = -ENOMEM; - goto error; + pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id); + if (!pdev) + goto err_alloc; + + pdev->dev.parent = pdevinfo->parent; + ACPI_COMPANION_SET(&pdev->dev, pdevinfo->acpi_node.companion); + + 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; } - if (num) { - retval = platform_device_add_resources(pdev, res, num); - if (retval) - goto error; + 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_COMPANION_SET(&pdev->dev, NULL); + kfree(pdev->dev.dma_mask); + +err_alloc: + platform_device_put(pdev); + return ERR_PTR(ret); } - retval = platform_device_add(pdev); - if (retval) - goto error; - return pdev; - -error: - platform_device_put(pdev); - return ERR_PTR(retval); } -EXPORT_SYMBOL_GPL(platform_device_register_simple); +EXPORT_SYMBOL_GPL(platform_device_register_full); -/** - * platform_device_register_data - * @parent: parent device for the device we're adding - * @name: base name of the device we're adding - * @id: instance id - * @data: platform specific data for this platform device - * @size: size of platform specific data - * - * This function creates a simple platform device that requires minimal - * resource and memory management. Canned release function freeing memory - * allocated for the device allows drivers using such devices to be - * unloaded without waiting for the last reference to the device to be - * dropped. - */ -struct platform_device *platform_device_register_data( - struct device *parent, - const char *name, int id, - const void *data, size_t size) +static int platform_drv_probe(struct device *_dev) { - struct platform_device *pdev; - int retval; + struct platform_driver *drv = to_platform_driver(_dev->driver); + struct platform_device *dev = to_platform_device(_dev); + int ret; - pdev = platform_device_alloc(name, id); - if (!pdev) { - retval = -ENOMEM; - goto error; - } + acpi_dev_pm_attach(_dev, true); - pdev->dev.parent = parent; + ret = drv->probe(dev); + if (ret) + acpi_dev_pm_detach(_dev, true); - if (size) { - retval = platform_device_add_data(pdev, data, size); - if (retval) - goto error; + if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { + dev_warn(_dev, "probe deferral not supported\n"); + ret = -ENXIO; } - retval = platform_device_add(pdev); - if (retval) - goto error; - - return pdev; - -error: - platform_device_put(pdev); - return ERR_PTR(retval); -} -EXPORT_SYMBOL_GPL(platform_device_register_data); - -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); - - return drv->probe(dev); + return ret; } static int platform_drv_probe_fail(struct device *_dev) @@ -460,8 +522,12 @@ static int platform_drv_remove(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); + int ret; + + ret = drv->remove(dev); + acpi_dev_pm_detach(_dev, true); - return drv->remove(dev); + return ret; } static void platform_drv_shutdown(struct device *_dev) @@ -470,14 +536,18 @@ static void platform_drv_shutdown(struct device *_dev) struct platform_device *dev = to_platform_device(_dev); drv->shutdown(dev); + acpi_dev_pm_detach(_dev, true); } /** - * platform_driver_register + * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure + * @owner: owning module/driver */ -int platform_driver_register(struct platform_driver *drv) +int __platform_driver_register(struct platform_driver *drv, + struct module *owner) { + drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; @@ -488,10 +558,10 @@ int platform_driver_register(struct platform_driver *drv) return driver_register(&drv->driver); } -EXPORT_SYMBOL_GPL(platform_driver_register); +EXPORT_SYMBOL_GPL(__platform_driver_register); /** - * platform_driver_unregister + * platform_driver_unregister - unregister a driver for platform-level devices * @drv: platform driver structure */ void platform_driver_unregister(struct platform_driver *drv) @@ -514,6 +584,8 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); * into system-on-chip processors, where the controller devices have been * configured as part of board setup. * + * Note that this is incompatible with deferred probing. + * * Returns zero if the driver registered and bound to a device, else returns * a negative error code and with the driver not registered. */ @@ -522,6 +594,12 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, { int retval, code; + /* + * Prevent driver from requesting probe deferral to avoid further + * futile probe attempts. + */ + drv->prevent_deferred_probe = true; + /* make sure driver won't have bind/unbind attributes */ drv->driver.suppress_bind_attrs = true; @@ -535,12 +613,12 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, * if the probe was successful, and make sure any forced probes of * new devices fail. */ - spin_lock(&platform_bus_type.p->klist_drivers.k_lock); + spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); drv->probe = NULL; if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) retval = -ENODEV; drv->driver.probe = platform_drv_probe_fail; - spin_unlock(&platform_bus_type.p->klist_drivers.k_lock); + spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); if (code != retval) platform_driver_unregister(drv); @@ -548,6 +626,62 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, } EXPORT_SYMBOL_GPL(platform_driver_probe); +/** + * platform_create_bundle - register driver and create corresponding device + * @driver: platform driver structure + * @probe: the driver probe routine, probably from an __init section + * @res: set of resources that needs to be allocated for the device + * @n_res: number of resources + * @data: platform specific data for this platform device + * @size: size of platform specific data + * + * Use this in legacy-style modules that probe hardware directly and + * register a single platform device and corresponding platform driver. + * + * Returns &struct platform_device pointer on success, or ERR_PTR() on error. + */ +struct platform_device * __init_or_module platform_create_bundle( + struct platform_driver *driver, + int (*probe)(struct platform_device *), + struct resource *res, unsigned int n_res, + const void *data, size_t size) +{ + struct platform_device *pdev; + int error; + + pdev = platform_device_alloc(driver->driver.name, -1); + if (!pdev) { + error = -ENOMEM; + goto err_out; + } + + error = platform_device_add_resources(pdev, res, n_res); + if (error) + goto err_pdev_put; + + error = platform_device_add_data(pdev, data, size); + if (error) + goto err_pdev_put; + + error = platform_device_add(pdev); + if (error) + goto err_pdev_put; + + error = platform_driver_probe(driver, probe); + if (error) + goto err_pdev_del; + + return pdev; + +err_pdev_del: + platform_device_del(pdev); +err_pdev_put: + platform_device_put(pdev); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(platform_create_bundle); + /* modalias support enables more hands-off userspace setup: * (a) environment variable lets new-style hotplug events work once system is * fully running: "modprobe $MODALIAS" @@ -558,27 +692,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { struct platform_device *pdev = to_platform_device(dev); - int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name); + int len; + + len = of_device_get_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + + len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name); return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } +static DEVICE_ATTR_RO(modalias); -static struct device_attribute platform_dev_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL, +static struct attribute *platform_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, }; +ATTRIBUTE_GROUPS(platform_dev); static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) { struct platform_device *pdev = to_platform_device(dev); + int rc; + + /* Some devices have extra OF data and an OF-style MODALIAS */ + rc = of_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, - (pdev->id_entry) ? pdev->id_entry->name : pdev->name); + pdev->name); return 0; } static const struct platform_device_id *platform_match_id( - struct platform_device_id *id, + const struct platform_device_id *id, struct platform_device *pdev) { while (id->name[0]) { @@ -609,7 +765,15 @@ static int platform_match(struct device *dev, struct device_driver *drv) struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); - /* match against the id table first */ + /* Attempt an OF style match first */ + if (of_driver_match_device(dev, drv)) + return 1; + + /* Then try ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; @@ -643,35 +807,11 @@ static int platform_legacy_resume(struct device *dev) return ret; } -static int platform_pm_prepare(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (drv && drv->pm && drv->pm->prepare) - ret = drv->pm->prepare(dev); - - return ret; -} - -static void platform_pm_complete(struct device *dev) -{ - struct device_driver *drv = dev->driver; - - if (drv && drv->pm && drv->pm->complete) - drv->pm->complete(dev); -} - -#else /* !CONFIG_PM_SLEEP */ - -#define platform_pm_prepare NULL -#define platform_pm_complete NULL - -#endif /* !CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_SUSPEND -static int platform_pm_suspend(struct device *dev) +int platform_pm_suspend(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -689,23 +829,7 @@ static int platform_pm_suspend(struct device *dev) return ret; } -static int platform_pm_suspend_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->suspend_noirq) - ret = drv->pm->suspend_noirq(dev); - } - - return ret; -} - -static int platform_pm_resume(struct device *dev) +int platform_pm_resume(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -723,34 +847,11 @@ static int platform_pm_resume(struct device *dev) return ret; } -static int platform_pm_resume_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->resume_noirq) - ret = drv->pm->resume_noirq(dev); - } - - return ret; -} - -#else /* !CONFIG_SUSPEND */ - -#define platform_pm_suspend NULL -#define platform_pm_resume NULL -#define platform_pm_suspend_noirq NULL -#define platform_pm_resume_noirq NULL - -#endif /* !CONFIG_SUSPEND */ +#endif /* CONFIG_SUSPEND */ -#ifdef CONFIG_HIBERNATION +#ifdef CONFIG_HIBERNATE_CALLBACKS -static int platform_pm_freeze(struct device *dev) +int platform_pm_freeze(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -768,23 +869,7 @@ static int platform_pm_freeze(struct device *dev) return ret; } -static int platform_pm_freeze_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->freeze_noirq) - ret = drv->pm->freeze_noirq(dev); - } - - return ret; -} - -static int platform_pm_thaw(struct device *dev) +int platform_pm_thaw(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -802,23 +887,7 @@ static int platform_pm_thaw(struct device *dev) return ret; } -static int platform_pm_thaw_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->thaw_noirq) - ret = drv->pm->thaw_noirq(dev); - } - - return ret; -} - -static int platform_pm_poweroff(struct device *dev) +int platform_pm_poweroff(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -836,23 +905,7 @@ static int platform_pm_poweroff(struct device *dev) return ret; } -static int platform_pm_poweroff_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->poweroff_noirq) - ret = drv->pm->poweroff_noirq(dev); - } - - return ret; -} - -static int platform_pm_restore(struct device *dev) +int platform_pm_restore(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -870,83 +923,17 @@ static int platform_pm_restore(struct device *dev) return ret; } -static int platform_pm_restore_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->restore_noirq) - ret = drv->pm->restore_noirq(dev); - } - - return ret; -} - -#else /* !CONFIG_HIBERNATION */ - -#define platform_pm_freeze NULL -#define platform_pm_thaw NULL -#define platform_pm_poweroff NULL -#define platform_pm_restore NULL -#define platform_pm_freeze_noirq NULL -#define platform_pm_thaw_noirq NULL -#define platform_pm_poweroff_noirq NULL -#define platform_pm_restore_noirq NULL - -#endif /* !CONFIG_HIBERNATION */ - -#ifdef CONFIG_PM_RUNTIME - -int __weak platform_pm_runtime_suspend(struct device *dev) -{ - return -ENOSYS; -}; - -int __weak platform_pm_runtime_resume(struct device *dev) -{ - return -ENOSYS; -}; - -int __weak platform_pm_runtime_idle(struct device *dev) -{ - return -ENOSYS; -}; - -#else /* !CONFIG_PM_RUNTIME */ - -#define platform_pm_runtime_suspend NULL -#define platform_pm_runtime_resume NULL -#define platform_pm_runtime_idle NULL - -#endif /* !CONFIG_PM_RUNTIME */ +#endif /* CONFIG_HIBERNATE_CALLBACKS */ static const struct dev_pm_ops platform_dev_pm_ops = { - .prepare = platform_pm_prepare, - .complete = platform_pm_complete, - .suspend = platform_pm_suspend, - .resume = platform_pm_resume, - .freeze = platform_pm_freeze, - .thaw = platform_pm_thaw, - .poweroff = platform_pm_poweroff, - .restore = platform_pm_restore, - .suspend_noirq = platform_pm_suspend_noirq, - .resume_noirq = platform_pm_resume_noirq, - .freeze_noirq = platform_pm_freeze_noirq, - .thaw_noirq = platform_pm_thaw_noirq, - .poweroff_noirq = platform_pm_poweroff_noirq, - .restore_noirq = platform_pm_restore_noirq, - .runtime_suspend = platform_pm_runtime_suspend, - .runtime_resume = platform_pm_runtime_resume, - .runtime_idle = platform_pm_runtime_idle, + .runtime_suspend = pm_generic_runtime_suspend, + .runtime_resume = pm_generic_runtime_resume, + USE_PLATFORM_PM_SLEEP_OPS }; struct bus_type platform_bus_type = { .name = "platform", - .dev_attrs = platform_dev_attrs, + .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, @@ -994,9 +981,11 @@ static __initdata LIST_HEAD(early_platform_driver_list); static __initdata LIST_HEAD(early_platform_device_list); /** - * early_platform_driver_register + * early_platform_driver_register - register early platform driver * @epdrv: early_platform driver structure * @buf: string passed from early_param() + * + * Helper function for early_platform_init() / early_platform_init_buffer() */ int __init early_platform_driver_register(struct early_platform_driver *epdrv, char *buf) @@ -1048,9 +1037,12 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv, } /** - * early_platform_add_devices - add a numbers of early platform devices + * early_platform_add_devices - adds a number of early platform devices * @devs: array of early platform devices to add * @num: number of early platform devices in array + * + * Used by early architecture code to register early platform devices and + * their platform data. */ void __init early_platform_add_devices(struct platform_device **devs, int num) { @@ -1062,6 +1054,7 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) dev = &devs[i]->dev; if (!dev->devres_head.next) { + pm_runtime_early_init(dev); INIT_LIST_HEAD(&dev->devres_head); list_add_tail(&dev->devres_head, &early_platform_device_list); @@ -1070,8 +1063,12 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) } /** - * early_platform_driver_register_all + * early_platform_driver_register_all - register early platform drivers * @class_str: string to identify early platform driver class + * + * Used by architecture code to register all early platform drivers + * for a certain class. If omitted then only early platform drivers + * with matching kernel command line class parameters will be registered. */ void __init early_platform_driver_register_all(char *class_str) { @@ -1093,11 +1090,11 @@ void __init early_platform_driver_register_all(char *class_str) } /** - * early_platform_match + * early_platform_match - find early platform device matching driver * @epdrv: early platform driver structure * @id: id to match against */ -static __init struct platform_device * +static struct platform_device * __init early_platform_match(struct early_platform_driver *epdrv, int id) { struct platform_device *pd; @@ -1111,11 +1108,11 @@ early_platform_match(struct early_platform_driver *epdrv, int id) } /** - * early_platform_left + * early_platform_left - check if early platform driver has matching devices * @epdrv: early platform driver structure * @id: return true if id or above exists */ -static __init int early_platform_left(struct early_platform_driver *epdrv, +static int __init early_platform_left(struct early_platform_driver *epdrv, int id) { struct platform_device *pd; @@ -1129,7 +1126,7 @@ static __init int early_platform_left(struct early_platform_driver *epdrv, } /** - * early_platform_driver_probe_id + * early_platform_driver_probe_id - probe drivers matching class_str and id * @class_str: string to identify early platform driver class * @id: id to match against * @nr_probe: number of platform devices to successfully probe before exiting @@ -1170,8 +1167,8 @@ static int __init early_platform_driver_probe_id(char *class_str, switch (match_id) { case EARLY_PLATFORM_ID_ERROR: - pr_warning("%s: unable to parse %s parameter\n", - class_str, epdrv->pdrv->driver.name); + pr_warn("%s: unable to parse %s parameter\n", + class_str, epdrv->pdrv->driver.name); /* fall-through */ case EARLY_PLATFORM_ID_UNSET: match = NULL; @@ -1181,9 +1178,29 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (match) { + /* + * Set up a sensible init_name to enable + * dev_name() and others to be used before the + * rest of the driver core is initialized. + */ + if (!match->dev.init_name && slab_is_available()) { + if (match->id != -1) + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s.%d", + match->name, + match->id); + else + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s", + match->name); + + if (!match->dev.init_name) + return -ENOMEM; + } + if (epdrv->pdrv->probe(match)) - pr_warning("%s: unable to probe %s early.\n", - class_str, match->name); + pr_warn("%s: unable to probe %s early.\n", + class_str, match->name); else n++; } @@ -1199,10 +1216,14 @@ static int __init early_platform_driver_probe_id(char *class_str, } /** - * early_platform_driver_probe + * early_platform_driver_probe - probe a class of registered drivers * @class_str: string to identify early platform driver class * @nr_probe: number of platform devices to successfully probe before exiting * @user_only: only probe user specified early platform devices + * + * Used by architecture code to probe registered early platform drivers + * within a certain class. For probe to happen a registered early platform + * device matching a registered early platform driver is needed. */ int __init early_platform_driver_probe(char *class_str, int nr_probe, |
